Aspectos definitorios de las funciones

Beginner

This tutorial is from open-source community. Access the source code

Introducción

En este laboratorio, aprenderás a explorar los aspectos fundamentales de las funciones y métodos de Python. También harás que las funciones sean más flexibles mediante el diseño efectivo de parámetros.

Además, implementarás sugerencias de tipo (type hints) para mejorar la legibilidad y seguridad del código, lo cual es crucial para escribir código Python de alta calidad.

Este es un Guided Lab, que proporciona instrucciones paso a paso para ayudarte a aprender y practicar. Sigue las instrucciones cuidadosamente para completar cada paso y obtener experiencia práctica. Los datos históricos muestran que este es un laboratorio de nivel principiante con una tasa de finalización del 91%. Ha recibido una tasa de reseñas positivas del 100% por parte de los estudiantes.

Comprendiendo el contexto

En ejercicios anteriores, es posible que hayas encontrado código que lee archivos CSV y almacena los datos en diversas estructuras de datos. El propósito de este código es tomar los datos de texto sin procesar de un archivo CSV y convertirlos en objetos de Python más útiles, como diccionarios o instancias de clase. Esta conversión es esencial porque nos permite trabajar con los datos de una manera más estructurada y significativa dentro de nuestros programas de Python.

El patrón típico para leer archivos CSV suele seguir una estructura específica. Aquí tienes un ejemplo de una función que lee un archivo CSV y convierte cada fila en un diccionario:

import csv

def read_csv_as_dicts(filename, types):
    records = []
    with open(filename) as file:
        rows = csv.reader(file)
        headers = next(rows)
        for row in rows:
            record = { name: func(val)
                       for name, func, val in zip(headers, types, row) }
            records.append(record)
    return records

Analicemos cómo funciona esta función. En primer lugar, importa el módulo csv, que proporciona funcionalidades para trabajar con archivos CSV en Python. La función toma dos parámetros: filename, que es el nombre del archivo CSV a leer, y types, que es una lista de funciones utilizadas para convertir los datos de cada columna al tipo de dato adecuado.

Dentro de la función, se inicializa una lista vacía llamada records para almacenar los diccionarios que representan cada fila del archivo CSV. Luego, se abre el archivo utilizando la declaración with, que asegura que el archivo se cierre correctamente después de ejecutar el bloque de código. La función csv.reader se utiliza para crear un iterador que lee cada fila del archivo CSV. Se asume que la primera fila son los encabezados, por lo que se recupera utilizando la función next.

A continuación, la función itera sobre las filas restantes del archivo CSV. Para cada fila, crea un diccionario utilizando una comprensión de diccionario. Las claves del diccionario son los encabezados de las columnas, y los valores son el resultado de aplicar la función de conversión de tipo correspondiente de la lista types al valor de la fila. Finalmente, el diccionario se agrega a la lista records, y la función devuelve la lista de diccionarios.

Ahora, veamos una función similar que lee datos de un archivo CSV en instancias de clase:

def read_csv_as_instances(filename, cls):
    records = []
    with open(filename) as file:
        rows = csv.reader(file)
        headers = next(rows)
        for row in rows:
            record = cls.from_row(row)
            records.append(record)
    return records

Esta función es similar a la anterior, pero en lugar de crear diccionarios, crea instancias de una clase. La función toma dos parámetros: filename, que es el nombre del archivo CSV a leer, y cls, que es la clase cuyas instancias se crearán.

Dentro de la función, sigue una estructura similar a la función anterior. Inicializa una lista vacía llamada records para almacenar las instancias de clase. Luego, abre el archivo, lee los encabezados y itera sobre las filas restantes. Para cada fila, llama al método from_row de la clase cls para crear una instancia de la clase utilizando los datos de la fila. La instancia se agrega a la lista records, y la función devuelve la lista de instancias.

En este laboratorio, refactorizaremos estas funciones para hacerlas más flexibles y robustas. También exploraremos el sistema de sugerencias de tipo (type hinting) de Python, que nos permite especificar los tipos esperados de los parámetros y valores de retorno de nuestras funciones. Esto puede hacer que nuestro código sea más legible y fácil de entender, especialmente para otros desarrolladores que puedan trabajar con nuestro código.

Comencemos creando un archivo reader.py y agregando estas funciones iniciales a él. Asegúrate de probar estas funciones para garantizar que funcionen correctamente antes de pasar a los siguientes pasos.

Creando las funciones básicas de lectura de archivos CSV

Comencemos creando un archivo reader.py con dos funciones básicas para leer datos de archivos CSV. Estas funciones nos ayudarán a manejar archivos CSV de diferentes maneras, como convertir los datos en diccionarios o instancias de clase.

Primero, necesitamos entender qué es un archivo CSV. CSV significa Valores Separados por Comas (Comma-Separated Values). Es un formato de archivo simple utilizado para almacenar datos tabulares, donde cada línea representa una fila y los valores en cada fila están separados por comas.

Ahora, creemos el archivo reader.py. Sigue estos pasos:

  1. Abre el editor de código y crea un nuevo archivo llamado reader.py en el directorio /home/labex/project. Aquí es donde escribiremos nuestras funciones para leer datos de archivos CSV.

  2. Agrega el siguiente código a reader.py:

## reader.py

import csv

def read_csv_as_dicts(filename, types):
    '''
    Read CSV data into a list of dictionaries with optional type conversion

    Args:
        filename: Path to the CSV file
        types: List of type conversion functions for each column

    Returns:
        List of dictionaries with data from the CSV file
    '''
    records = []
    with open(filename) as file:
        rows = csv.reader(file)
        headers = next(rows)
        for row in rows:
            record = { name: func(val)
                       for name, func, val in zip(headers, types, row) }
            records.append(record)
    return records

def read_csv_as_instances(filename, cls):
    '''
    Read CSV data into a list of class instances

    Args:
        filename: Path to the CSV file
        cls: Class to create instances from

    Returns:
        List of class instances with data from the CSV file
    '''
    records = []
    with open(filename) as file:
        rows = csv.reader(file)
        headers = next(rows)
        for row in rows:
            record = cls.from_row(row)
            records.append(record)
    return records

En la función read_csv_as_dicts, primero abrimos el archivo CSV utilizando la función open. Luego, usamos csv.reader para leer el archivo línea por línea. La declaración next(rows) lee la primera línea del archivo, que generalmente contiene los encabezados. Después de eso, iteramos sobre las filas restantes. Para cada fila, creamos un diccionario donde las claves son los encabezados y los valores son los valores correspondientes en la fila, con una conversión de tipo opcional utilizando la lista types.

La función read_csv_as_instances es similar, pero en lugar de crear diccionarios, crea instancias de una clase dada. Asume que la clase tiene un método estático llamado from_row que puede crear una instancia a partir de una fila de datos.

  1. Probemos estas funciones para asegurarnos de que funcionen correctamente. Crea un nuevo archivo llamado test_reader.py con el siguiente código:
## test_reader.py

import reader
import stock

## Test reading CSV as dictionaries
portfolio_dicts = reader.read_csv_as_dicts('portfolio.csv', [str, int, float])
print("First portfolio item as dictionary:", portfolio_dicts[0])
print("Total items:", len(portfolio_dicts))

## Test reading CSV as class instances
portfolio_instances = reader.read_csv_as_instances('portfolio.csv', stock.Stock)
print("\nFirst portfolio item as Stock instance:", portfolio_instances[0])
print("Total items:", len(portfolio_instances))

En el archivo test_reader.py, importamos el módulo reader que acabamos de crear y el módulo stock. Luego probamos las dos funciones llamándolas con un archivo CSV de muestra llamado portfolio.csv. Imprimimos el primer elemento y el número total de elementos en la cartera para verificar que las funciones funcionen como se espera.

  1. Ejecuta el script de prueba desde la terminal:
python test_reader.py

La salida debería ser similar a esta:

First portfolio item as dictionary: {'name': 'AA', 'shares': 100, 'price': 32.2}
Total items: 7

First portfolio item as Stock instance: Stock('AA', 100, 32.2)
Total items: 7

Esto confirma que nuestras dos funciones funcionan correctamente. La primera función convierte los datos de un archivo CSV en una lista de diccionarios con una conversión de tipo adecuada, y la segunda función crea instancias de clase utilizando un método estático en la clase proporcionada.

En el siguiente paso, refactorizaremos estas funciones para hacerlas más flexibles permitiéndoles trabajar con cualquier fuente iterable de datos, no solo con nombres de archivos.

Haciendo las funciones más flexibles

Actualmente, nuestras funciones se limitan a leer desde archivos especificados por un nombre de archivo. Esto restringe su usabilidad. En programación, a menudo es beneficioso hacer que las funciones sean más flexibles para que puedan manejar diferentes tipos de entrada. En nuestro caso, sería genial si nuestras funciones pudieran trabajar con cualquier iterable que produzca líneas, como objetos de archivo u otras fuentes. De esta manera, podemos usar estas funciones en más escenarios, como leer desde archivos comprimidos u otros flujos de datos.

Refactoricemos nuestro código para habilitar esta flexibilidad:

  1. Abre el archivo reader.py. Lo vamos a modificar para incluir algunas nuevas funciones. Estas nuevas funciones permitirán que nuestro código funcione con diferentes tipos de iterables. Aquí está el código que debes agregar:
## reader.py

import csv

def csv_as_dicts(lines, types):
    '''
    Parse CSV data from an iterable into a list of dictionaries

    Args:
        lines: An iterable producing CSV lines
        types: List of type conversion functions for each column

    Returns:
        List of dictionaries with data from the CSV lines
    '''
    records = []
    rows = csv.reader(lines)
    headers = next(rows)
    for row in rows:
        record = { name: func(val)
                  for name, func, val in zip(headers, types, row) }
        records.append(record)
    return records

def csv_as_instances(lines, cls):
    '''
    Parse CSV data from an iterable into a list of class instances

    Args:
        lines: An iterable producing CSV lines
        cls: Class to create instances from

    Returns:
        List of class instances with data from the CSV lines
    '''
    records = []
    rows = csv.reader(lines)
    headers = next(rows)
    for row in rows:
        record = cls.from_row(row)
        records.append(record)
    return records

def read_csv_as_dicts(filename, types):
    '''
    Read CSV data into a list of dictionaries with optional type conversion

    Args:
        filename: Path to the CSV file
        types: List of type conversion functions for each column

    Returns:
        List of dictionaries with data from the CSV file
    '''
    with open(filename) as file:
        return csv_as_dicts(file, types)

def read_csv_as_instances(filename, cls):
    '''
    Read CSV data into a list of class instances

    Args:
        filename: Path to the CSV file
        cls: Class to create instances from

    Returns:
        List of class instances with data from the CSV file
    '''
    with open(filename) as file:
        return csv_as_instances(file, cls)

Veamos de cerca cómo hemos refactorizado el código:

  1. Hemos creado dos funciones más genéricas, csv_as_dicts() y csv_as_instances(). Estas funciones están diseñadas para trabajar con cualquier iterable que produzca líneas de CSV. Esto significa que pueden manejar diferentes tipos de fuentes de entrada, no solo archivos especificados por un nombre de archivo.

  2. Hemos reimplementado read_csv_as_dicts() y read_csv_as_instances() para usar estas nuevas funciones. De esta manera, la funcionalidad original de leer desde un archivo por nombre de archivo sigue estando disponible, pero ahora se basa en las funciones más flexibles.

  3. Este enfoque mantiene la compatibilidad hacia atrás con el código existente. Eso significa que cualquier código que estaba usando las funciones antiguas seguirá funcionando como se espera. Al mismo tiempo, nuestra biblioteca se vuelve más flexible porque ahora puede manejar diferentes tipos de fuentes de entrada.

  4. Ahora, probemos estas nuevas funciones. Crea un archivo llamado test_reader_flexibility.py y agrega el siguiente código a él. Este código probará las nuevas funciones con diferentes tipos de fuentes de entrada:

## test_reader_flexibility.py

import reader
import stock
import gzip

## Test opening a regular file
with open('portfolio.csv') as file:
    portfolio = reader.csv_as_dicts(file, [str, int, float])
    print("First item from open file:", portfolio[0])

## Test opening a gzipped file
with gzip.open('portfolio.csv.gz', 'rt') as file:  ## 'rt' means read text
    portfolio = reader.csv_as_instances(file, stock.Stock)
    print("\nFirst item from gzipped file:", portfolio[0])

## Test backward compatibility
portfolio = reader.read_csv_as_dicts('portfolio.csv', [str, int, float])
print("\nFirst item using backward compatible function:", portfolio[0])
  1. Después de crear el archivo de prueba, necesitamos ejecutar el script de prueba desde la terminal. Abre tu terminal y navega hasta el directorio donde se encuentra el archivo test_reader_flexibility.py. Luego ejecuta el siguiente comando:
python test_reader_flexibility.py

La salida debería ser similar a esta:

First item from open file: {'name': 'AA', 'shares': 100, 'price': 32.2}

First item from gzipped file: Stock('AA', 100, 32.2)

First item using backward compatible function: {'name': 'AA', 'shares': 100, 'price': 32.2}

Esta salida confirma que nuestras funciones ahora funcionan con diferentes tipos de fuentes de entrada mientras mantienen la compatibilidad hacia atrás. Las funciones refactorizadas pueden procesar datos de:

  • Archivos regulares abiertos con open()
  • Archivos comprimidos abiertos con gzip.open()
  • Cualquier otro objeto iterable que produzca líneas de texto

Esto hace que nuestro código sea mucho más flexible y fácil de usar en diferentes escenarios.

Manejo de archivos CSV sin encabezados

En el mundo del procesamiento de datos, no todos los archivos CSV tienen encabezados en su primera fila. Los encabezados son los nombres dados a cada columna en un archivo CSV, que nos ayudan a entender qué tipo de datos contiene cada columna. Cuando un archivo CSV carece de encabezados, necesitamos una forma de manejarlo adecuadamente. En esta sección, modificaremos nuestras funciones para permitir que el llamante proporcione los encabezados manualmente, de modo que podamos trabajar con archivos CSV tanto con como sin encabezados.

  1. Abre el archivo reader.py y actualízalo para incluir el manejo de encabezados:
## reader.py

import csv

def csv_as_dicts(lines, types, headers=None):
    '''
    Parse CSV data from an iterable into a list of dictionaries

    Args:
        lines: An iterable producing CSV lines
        types: List of type conversion functions for each column
        headers: Optional list of column names. If None, first row is used as headers

    Returns:
        List of dictionaries with data from the CSV lines
    '''
    records = []
    rows = csv.reader(lines)

    if headers is None:
        ## Use the first row as headers if none provided
        headers = next(rows)

    for row in rows:
        record = { name: func(val)
                  for name, func, val in zip(headers, types, row) }
        records.append(record)
    return records

def csv_as_instances(lines, cls, headers=None):
    '''
    Parse CSV data from an iterable into a list of class instances

    Args:
        lines: An iterable producing CSV lines
        cls: Class to create instances from
        headers: Optional list of column names. If None, first row is used as headers

    Returns:
        List of class instances with data from the CSV lines
    '''
    records = []
    rows = csv.reader(lines)

    if headers is None:
        ## Skip the first row if no headers provided
        next(rows)

    for row in rows:
        record = cls.from_row(row)
        records.append(record)
    return records

def read_csv_as_dicts(filename, types, headers=None):
    '''
    Read CSV data into a list of dictionaries with optional type conversion

    Args:
        filename: Path to the CSV file
        types: List of type conversion functions for each column
        headers: Optional list of column names. If None, first row is used as headers

    Returns:
        List of dictionaries with data from the CSV file
    '''
    with open(filename) as file:
        return csv_as_dicts(file, types, headers)

def read_csv_as_instances(filename, cls, headers=None):
    '''
    Read CSV data into a list of class instances

    Args:
        filename: Path to the CSV file
        cls: Class to create instances from
        headers: Optional list of column names. If None, first row is used as headers

    Returns:
        List of class instances with data from the CSV file
    '''
    with open(filename) as file:
        return csv_as_instances(file, cls, headers)

Comprendamos los cambios clave que hemos realizado en estas funciones:

  1. Hemos agregado un parámetro headers a todas las funciones y hemos establecido su valor predeterminado en None. Esto significa que si el llamante no proporciona ningún encabezado, las funciones utilizarán el comportamiento predeterminado.

  2. En la función csv_as_dicts, utilizamos la primera fila como encabezados solo si el parámetro headers es None. Esto nos permite manejar automáticamente archivos con encabezados.

  3. En la función csv_as_instances, omitimos la primera fila solo si el parámetro headers es None. Esto se debe a que si estamos proporcionando nuestros propios encabezados, la primera fila del archivo es datos reales, no encabezados.

  4. Probemos estas modificaciones con nuestro archivo sin encabezados. Crea un archivo llamado test_headers.py:

## test_headers.py

import reader
import stock

## Define column names for the file without headers
column_names = ['name', 'shares', 'price']

## Test reading a file without headers
portfolio = reader.read_csv_as_dicts('portfolio_noheader.csv',
                                     [str, int, float],
                                     headers=column_names)
print("First item from file without headers:", portfolio[0])
print("Total items:", len(portfolio))

## Test reading the same file as instances
portfolio = reader.read_csv_as_instances('portfolio_noheader.csv',
                                        stock.Stock,
                                        headers=column_names)
print("\nFirst item as Stock instance:", portfolio[0])
print("Total items:", len(portfolio))

## Verify that original functionality still works
portfolio = reader.read_csv_as_dicts('portfolio.csv', [str, int, float])
print("\nFirst item from file with headers:", portfolio[0])

En este script de prueba, primero definimos los nombres de las columnas para el archivo sin encabezados. Luego probamos leer el archivo sin encabezados como una lista de diccionarios y como una lista de instancias de clase. Finalmente, verificamos que la funcionalidad original siga funcionando leyendo un archivo con encabezados.

  1. Ejecuta el script de prueba desde la terminal:
python test_headers.py

La salida debería ser similar a:

First item from file without headers: {'name': 'AA', 'shares': 100, 'price': 32.2}
Total items: 7

First item as Stock instance: Stock('AA', 100, 32.2)
Total items: 7

First item from file with headers: {'name': 'AA', 'shares': 100, 'price': 32.2}

Esta salida confirma que nuestras funciones ahora pueden manejar archivos CSV tanto con como sin encabezados. El usuario puede proporcionar nombres de columnas cuando sea necesario o confiar en el comportamiento predeterminado de leer los encabezados de la primera fila.

Con esta modificación, nuestras funciones de lectura de archivos CSV son ahora más versátiles y pueden manejar una gama más amplia de formatos de archivos. Este es un paso importante para hacer que nuestro código sea más robusto y útil en diferentes escenarios.

Agregando sugerencias de tipo

En Python 3.5 y versiones posteriores, se admiten las sugerencias de tipo (type hints). Las sugerencias de tipo son una forma de indicar los tipos de datos esperados de variables, parámetros de función y valores de retorno en tu código. No cambian cómo se ejecuta el código, pero hacen que el código sea más legible y pueden ayudar a detectar ciertos tipos de errores antes de que el código se ejecute realmente. Ahora, agreguemos sugerencias de tipo a nuestras funciones de lectura de archivos CSV.

  1. Abre el archivo reader.py y actualízalo para incluir sugerencias de tipo:
## reader.py

import csv
from typing import List, Callable, Dict, Any, Type, Optional, TextIO, Iterator, TypeVar

## Define a generic type for the class parameter
T = TypeVar('T')

def csv_as_dicts(lines: Iterator[str],
                types: List[Callable[[str], Any]],
                headers: Optional[List[str]] = None) -> List[Dict[str, Any]]:
    '''
    Parse CSV data from an iterable into a list of dictionaries

    Args:
        lines: An iterable producing CSV lines
        types: List of type conversion functions for each column
        headers: Optional list of column names. If None, first row is used as headers

    Returns:
        List of dictionaries with data from the CSV lines
    '''
    records: List[Dict[str, Any]] = []
    rows = csv.reader(lines)

    if headers is None:
        ## Use the first row as headers if none provided
        headers = next(rows)

    for row in rows:
        record = { name: func(val)
                  for name, func, val in zip(headers, types, row) }
        records.append(record)
    return records

def csv_as_instances(lines: Iterator[str],
                    cls: Type[T],
                    headers: Optional[List[str]] = None) -> List[T]:
    '''
    Parse CSV data from an iterable into a list of class instances

    Args:
        lines: An iterable producing CSV lines
        cls: Class to create instances from
        headers: Optional list of column names. If None, first row is used as headers

    Returns:
        List of class instances with data from the CSV lines
    '''
    records: List[T] = []
    rows = csv.reader(lines)

    if headers is None:
        ## Skip the first row if no headers provided
        next(rows)

    for row in rows:
        record = cls.from_row(row)
        records.append(record)
    return records

def read_csv_as_dicts(filename: str,
                     types: List[Callable[[str], Any]],
                     headers: Optional[List[str]] = None) -> List[Dict[str, Any]]:
    '''
    Read CSV data into a list of dictionaries with optional type conversion

    Args:
        filename: Path to the CSV file
        types: List of type conversion functions for each column
        headers: Optional list of column names. If None, first row is used as headers

    Returns:
        List of dictionaries with data from the CSV file
    '''
    with open(filename) as file:
        return csv_as_dicts(file, types, headers)

def read_csv_as_instances(filename: str,
                         cls: Type[T],
                         headers: Optional[List[str]] = None) -> List[T]:
    '''
    Read CSV data into a list of class instances

    Args:
        filename: Path to the CSV file
        cls: Class to create instances from
        headers: Optional list of column names. If None, first row is used as headers

    Returns:
        List of class instances with data from the CSV file
    '''
    with open(filename) as file:
        return csv_as_instances(file, cls, headers)

Comprendamos los cambios clave que hemos realizado en el código:

  1. Importamos tipos del módulo typing. Este módulo proporciona un conjunto de tipos que podemos usar para definir sugerencias de tipo. Por ejemplo, List, Dict y Optional son tipos de este módulo.

  2. Agregamos una variable de tipo genérico T para representar el tipo de clase. Una variable de tipo genérico nos permite escribir funciones que pueden trabajar con diferentes tipos de manera segura en términos de tipos.

  3. Agregamos sugerencias de tipo a todos los parámetros de función y valores de retorno. Esto hace claro qué tipos de argumentos espera una función y qué tipo de valor devuelve.

  4. Usamos tipos de contenedores adecuados como List, Dict y Optional. List representa una lista, Dict representa un diccionario y Optional indica que un parámetro puede tener un cierto tipo o ser None.

  5. Usamos Callable para las funciones de conversión de tipo. Callable se utiliza para indicar que un parámetro es una función que se puede llamar.

  6. Usamos el genérico T para expresar que csv_as_instances devuelve una lista de instancias de la clase pasada. Esto ayuda al IDE y otras herramientas a entender el tipo de los objetos devueltos.

  7. Ahora, creemos un archivo de prueba simple para asegurarnos de que todo siga funcionando correctamente:

## test_types.py

import reader
import stock

## The functions should work exactly as before
portfolio = reader.read_csv_as_dicts('portfolio.csv', [str, int, float])
print("First item:", portfolio[0])

## But now we have better type checking and IDE support
stock_portfolio = reader.read_csv_as_instances('portfolio.csv', stock.Stock)
print("\nFirst stock:", stock_portfolio[0])

## We can see that stock_portfolio is a list of Stock objects
## This helps IDEs provide better code completion
first_stock = stock_portfolio[0]
print(f"\nName: {first_stock.name}")
print(f"Shares: {first_stock.shares}")
print(f"Price: {first_stock.price}")
print(f"Value: {first_stock.shares * first_stock.price}")
  1. Ejecuta el script de prueba desde la terminal:
python test_types.py

La salida debería ser similar a:

First item: {'name': 'AA', 'shares': 100, 'price': 32.2}

First stock: Stock('AA', 100, 32.2)

Name: AA
Shares: 100
Price: 32.2
Value: 3220.0

Las sugerencias de tipo no cambian cómo se ejecuta el código, pero ofrecen varios beneficios:

  1. Ofrecen mejor soporte del IDE con finalización de código. Cuando usas un IDE como PyCharm o VS Code, puede usar las sugerencias de tipo para sugerir los métodos y atributos correctos para tus variables.
  2. Proporcionan una documentación más clara sobre los tipos de parámetros y valores de retorno esperados. Solo con mirar la definición de la función, puedes saber qué tipos de argumentos espera y qué tipo de valor devuelve.
  3. Permiten usar verificadores de tipo estáticos como mypy para detectar errores temprano. Los verificadores de tipo estáticos analizan tu código sin ejecutarlo y pueden encontrar errores relacionados con los tipos antes de ejecutar el código.
  4. Mejoran la legibilidad y mantenibilidad del código. Cuando tú u otros desarrolladores vuelvan al código más tarde, es más fácil entender lo que está haciendo el código.

En una base de código grande, estos beneficios pueden reducir significativamente los errores y hacer que el código sea más fácil de entender y mantener.

Nota: Las sugerencias de tipo son opcionales en Python, pero se utilizan cada vez más en el código profesional. Bibliotecas como las de la biblioteca estándar de Python y muchos paquetes de terceros populares ahora incluyen sugerencias de tipo extensas.

Resumen

En este laboratorio, has aprendido varios aspectos clave del diseño de funciones en Python. Primero, has aprendido el diseño básico de funciones, específicamente cómo escribir funciones para procesar datos CSV en diversas estructuras de datos. También has explorado la flexibilidad de las funciones al refactorizarlas para que funcionen con cualquier fuente iterable, lo que mejora la versatilidad y reutilización del código.

Además, has dominado la adición de parámetros opcionales para manejar diferentes casos de uso, como archivos CSV con o sin encabezados, y el uso del sistema de sugerencias de tipo (type hinting) de Python para mejorar la legibilidad y mantenibilidad del código. Estas habilidades son cruciales para escribir código Python robusto, y a medida que tus programas se vuelvan más complejos, estos principios de diseño mantendrán tu código organizado y comprensible. Estas técnicas se pueden aplicar más allá del procesamiento de archivos CSV, lo que las hace valiosas en tu conjunto de herramientas de programación en Python.