Aspectos Definicionais de Funções

Beginner

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

Introdução

Neste laboratório, você aprenderá a explorar os aspectos fundamentais das funções e métodos Python. Você também tornará as funções mais flexíveis ao projetar parâmetros de forma eficaz.

Além disso, você implementará type hints (anotações de tipo) para aprimorar a legibilidade e a segurança do código, o que é crucial para escrever código Python de alta qualidade.

Este é um Lab Guiado, que fornece instruções passo a passo para ajudá-lo a aprender e praticar. Siga as instruções cuidadosamente para completar cada etapa e ganhar experiência prática. Dados históricos mostram que este é um laboratório de nível iniciante com uma taxa de conclusão de 91%. Recebeu uma taxa de avaliações positivas de 100% dos estudantes.

Compreendendo o Contexto

Em exercícios anteriores, você pode ter encontrado código que lê arquivos CSV e armazena os dados em várias estruturas de dados. O objetivo deste código é pegar dados de texto brutos de um arquivo CSV e convertê-los em objetos Python mais úteis, como dicionários ou instâncias de classe. Essa conversão é essencial porque nos permite trabalhar com os dados de uma forma mais estruturada e significativa dentro de nossos programas Python.

O padrão típico para leitura de arquivos CSV geralmente segue uma estrutura específica. Aqui está um exemplo de uma função que lê um arquivo CSV e converte cada linha em um dicionário:

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

Vamos detalhar como essa função funciona. Primeiro, ela importa o módulo csv, que fornece funcionalidade para trabalhar com arquivos CSV em Python. A função recebe dois parâmetros: filename, que é o nome do arquivo CSV a ser lido, e types, que é uma lista de funções usadas para converter os dados em cada coluna para o tipo de dados apropriado.

Dentro da função, ela inicializa uma lista vazia chamada records para armazenar os dicionários que representam cada linha do arquivo CSV. Em seguida, ela abre o arquivo usando a instrução with, que garante que o arquivo seja fechado corretamente após a execução do bloco de código. A função csv.reader é usada para criar um iterador que lê cada linha do arquivo CSV. A primeira linha é assumida como os cabeçalhos, então ela é recuperada usando a função next.

Em seguida, a função itera sobre as linhas restantes no arquivo CSV. Para cada linha, ela cria um dicionário usando uma compreensão de dicionário. As chaves do dicionário são os cabeçalhos das colunas, e os valores são o resultado da aplicação da função de conversão de tipo correspondente da lista types ao valor na linha. Finalmente, o dicionário é adicionado à lista records, e a função retorna a lista de dicionários.

Agora, vamos analisar uma função semelhante que lê dados de um arquivo CSV em instâncias de classe:

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 função é semelhante à anterior, mas em vez de criar dicionários, ela cria instâncias de uma classe. A função recebe dois parâmetros: filename, que é o nome do arquivo CSV a ser lido, e cls, que é a classe cujas instâncias serão criadas.

Dentro da função, ela segue uma estrutura semelhante à função anterior. Ela inicializa uma lista vazia chamada records para armazenar as instâncias da classe. Em seguida, ela abre o arquivo, lê os cabeçalhos e itera sobre as linhas restantes. Para cada linha, ela chama o método from_row da classe cls para criar uma instância da classe usando os dados da linha. A instância é então adicionada à lista records, e a função retorna a lista de instâncias.

Neste laboratório, vamos refatorar essas funções para torná-las mais flexíveis e robustas. Também exploraremos o sistema de type hinting (anotação de tipo) do Python, que nos permite especificar os tipos esperados dos parâmetros e valores de retorno de nossas funções. Isso pode tornar nosso código mais legível e fácil de entender, especialmente para outros desenvolvedores que podem estar trabalhando com nosso código.

Vamos começar criando um arquivo reader.py e adicionando essas funções iniciais a ele. Certifique-se de testar essas funções para garantir que elas funcionem corretamente antes de prosseguir para as próximas etapas.

Criando as Funções Básicas do Leitor CSV

Vamos começar criando um arquivo reader.py com duas funções básicas para ler dados CSV. Essas funções nos ajudarão a lidar com arquivos CSV de diferentes maneiras, como converter os dados em dicionários ou instâncias de classe.

Primeiro, precisamos entender o que é um arquivo CSV. CSV significa Comma-Separated Values (Valores Separados por Vírgula). É um formato de arquivo simples usado para armazenar dados tabulares, onde cada linha representa uma linha e os valores em cada linha são separados por vírgulas.

Agora, vamos criar o arquivo reader.py. Siga estas etapas:

  1. Abra o editor de código e crie um novo arquivo chamado reader.py no diretório /home/labex/project. É aqui que escreveremos nossas funções para ler dados CSV.

  2. Adicione o seguinte 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

Na função read_csv_as_dicts, primeiro abrimos o arquivo CSV usando a função open. Em seguida, usamos csv.reader para ler o arquivo linha por linha. A instrução next(rows) lê a primeira linha do arquivo, que geralmente contém os cabeçalhos. Depois disso, iteramos sobre as linhas restantes. Para cada linha, criamos um dicionário onde as chaves são os cabeçalhos e os valores são os valores correspondentes na linha, com conversão de tipo opcional usando a lista types.

A função read_csv_as_instances é semelhante, mas em vez de criar dicionários, ela cria instâncias de uma determinada classe. Ela assume que a classe tem um método estático chamado from_row que pode criar uma instância a partir de uma linha de dados.

  1. Vamos testar essas funções para garantir que elas funcionem corretamente. Crie um novo arquivo chamado test_reader.py com o seguinte 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))

No arquivo test_reader.py, importamos o módulo reader que acabamos de criar e o módulo stock. Em seguida, testamos as duas funções chamando-as com um arquivo CSV de amostra chamado portfolio.csv. Imprimimos o primeiro item e o número total de itens no portfólio para verificar se as funções estão funcionando conforme o esperado.

  1. Execute o script de teste no terminal:
python test_reader.py

A saída deve ser semelhante 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

Isso confirma que nossas duas funções estão funcionando corretamente. A primeira função converte dados CSV em uma lista de dicionários com a conversão de tipo adequada, e a segunda função cria instâncias de classe usando um método estático na classe fornecida.

Na próxima etapa, vamos refatorar essas funções para torná-las mais flexíveis, permitindo que elas funcionem com qualquer fonte de dados iterável, e não apenas com nomes de arquivos.

Tornando as Funções Mais Flexíveis

Atualmente, nossas funções são limitadas à leitura de arquivos especificados por um nome de arquivo. Isso restringe sua usabilidade. Em programação, é frequentemente benéfico tornar as funções mais flexíveis para que possam lidar com diferentes tipos de entrada. Em nosso caso, seria ótimo se nossas funções pudessem trabalhar com qualquer iterável que produza linhas, como objetos de arquivo ou outras fontes. Dessa forma, podemos usar essas funções em mais cenários, como leitura de arquivos compactados ou outros fluxos de dados.

Vamos refatorar nosso código para permitir essa flexibilidade:

  1. Abra o arquivo reader.py. Vamos modificá-lo para incluir algumas novas funções. Essas novas funções permitirão que nosso código trabalhe com diferentes tipos de iteráveis. Aqui está o código que você precisa adicionar:
## 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)

Vamos analisar mais de perto como refatoramos o código:

  1. Criamos duas funções mais genéricas, csv_as_dicts() e csv_as_instances(). Essas funções são projetadas para trabalhar com qualquer iterável que produza linhas CSV. Isso significa que elas podem lidar com diferentes tipos de fontes de entrada, não apenas arquivos especificados por um nome de arquivo.

  2. Reimplementamos read_csv_as_dicts() e read_csv_as_instances() para usar essas novas funções. Dessa forma, a funcionalidade original de leitura de um arquivo por nome de arquivo ainda está disponível, mas agora ela é construída sobre as funções mais flexíveis.

  3. Essa abordagem mantém a compatibilidade com versões anteriores com o código existente. Isso significa que qualquer código que estava usando as funções antigas ainda funcionará conforme o esperado. Ao mesmo tempo, nossa biblioteca se torna mais flexível porque agora pode lidar com diferentes tipos de fontes de entrada.

  4. Agora, vamos testar essas novas funções. Crie um arquivo chamado test_reader_flexibility.py e adicione o seguinte código a ele. Este código testará as novas funções com diferentes tipos de fontes 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. Depois de criar o arquivo de teste, precisamos executar o script de teste no terminal. Abra seu terminal e navegue até o diretório onde o arquivo test_reader_flexibility.py está localizado. Em seguida, execute o seguinte comando:
python test_reader_flexibility.py

A saída deve ser semelhante 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}

Essa saída confirma que nossas funções agora funcionam com diferentes tipos de fontes de entrada, mantendo a compatibilidade com versões anteriores. As funções refatoradas podem processar dados de:

  • Arquivos regulares abertos com open()
  • Arquivos compactados abertos com gzip.open()
  • Qualquer outro objeto iterável que produza linhas de texto

Isso torna nosso código muito mais flexível e fácil de usar em diferentes cenários.

Lidando com Arquivos CSV Sem Cabeçalhos

No mundo do processamento de dados, nem todos os arquivos CSV vêm com cabeçalhos em sua primeira linha. Os cabeçalhos são os nomes dados a cada coluna em um arquivo CSV, que nos ajudam a entender que tipo de dados cada coluna contém. Quando um arquivo CSV não possui cabeçalhos, precisamos de uma maneira de lidar com ele adequadamente. Nesta seção, modificaremos nossas funções para permitir que o chamador forneça os cabeçalhos manualmente, para que possamos trabalhar com arquivos CSV com e sem cabeçalhos.

  1. Abra o arquivo reader.py e atualize-o para incluir o tratamento de cabeçalhos:
## 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)

Vamos entender as principais mudanças que fizemos nessas funções:

  1. Adicionamos um parâmetro headers a todas as funções e definimos seu valor padrão como None. Isso significa que, se o chamador não fornecer nenhum cabeçalho, as funções usarão o comportamento padrão.

  2. Na função csv_as_dicts, usamos a primeira linha como cabeçalhos somente se o parâmetro headers for None. Isso nos permite lidar com arquivos com cabeçalhos automaticamente.

  3. Na função csv_as_instances, ignoramos a primeira linha somente se o parâmetro headers for None. Isso ocorre porque, se estivermos fornecendo nossos próprios cabeçalhos, a primeira linha do arquivo são dados reais, não cabeçalhos.

  4. Vamos testar essas modificações com nosso arquivo sem cabeçalhos. Crie um arquivo chamado 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])

Neste script de teste, primeiro definimos os nomes das colunas para o arquivo sem cabeçalhos. Em seguida, testamos a leitura do arquivo sem cabeçalhos como uma lista de dicionários e como uma lista de instâncias de classe. Finalmente, verificamos se a funcionalidade original ainda funciona lendo um arquivo com cabeçalhos.

  1. Execute o script de teste no terminal:
python test_headers.py

A saída deve ser semelhante 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}

Essa saída confirma que nossas funções agora podem lidar com arquivos CSV com e sem cabeçalhos. O usuário pode fornecer nomes de colunas quando necessário ou confiar no comportamento padrão de leitura de cabeçalhos da primeira linha.

Ao fazer essa modificação, nossas funções de leitura CSV agora são mais versáteis e podem lidar com uma gama maior de formatos de arquivo. Esta é uma etapa importante para tornar nosso código mais robusto e útil em diferentes cenários.

Adicionando Dicas de Tipo

No Python 3.5 e versões posteriores, as dicas de tipo (type hints) são suportadas. As dicas de tipo são uma maneira de indicar os tipos de dados esperados de variáveis, parâmetros de função e valores de retorno em seu código. Elas não alteram como o código é executado, mas tornam o código mais legível e podem ajudar a detectar certos tipos de erros antes que o código seja realmente executado. Agora, vamos adicionar dicas de tipo às nossas funções de leitura CSV.

  1. Abra o arquivo reader.py e atualize-o para incluir dicas 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)

Vamos entender as principais mudanças que fizemos no código:

  1. Importamos tipos do módulo typing. Este módulo fornece um conjunto de tipos que podemos usar para definir dicas de tipo. Por exemplo, List, Dict e Optional são tipos deste módulo.

  2. Adicionamos uma variável de tipo genérico T para representar o tipo de classe. Uma variável de tipo genérico nos permite escrever funções que podem trabalhar com diferentes tipos de forma segura para tipos.

  3. Adicionamos dicas de tipo a todos os parâmetros de função e valores de retorno. Isso deixa claro quais tipos de argumentos uma função espera e que tipo de valor ela retorna.

  4. Usamos tipos de contêiner apropriados como List, Dict e Optional. List representa uma lista, Dict representa um dicionário e Optional indica que um parâmetro pode ter um determinado tipo ou ser None.

  5. Usamos Callable para as funções de conversão de tipo. Callable é usado para indicar que um parâmetro é uma função que pode ser chamada.

  6. Usamos o genérico T para expressar que csv_as_instances retorna uma lista de instâncias da classe passada. Isso ajuda a IDE e outras ferramentas a entender o tipo dos objetos retornados.

  7. Agora, vamos criar um arquivo de teste simples para garantir que tudo ainda funcione corretamente:

## 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. Execute o script de teste no terminal:
python test_types.py

A saída deve ser semelhante 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

As dicas de tipo não alteram como o código é executado, mas fornecem vários benefícios:

  1. Elas oferecem melhor suporte de IDE com preenchimento de código. Quando você usa uma IDE como PyCharm ou VS Code, ela pode usar as dicas de tipo para sugerir os métodos e atributos corretos para suas variáveis.
  2. Elas fornecem uma documentação mais clara sobre os tipos de parâmetros e retorno esperados. Apenas olhando para a definição da função, você pode dizer que tipos de argumentos ela espera e que tipo de valor ela retorna.
  3. Elas permitem que você use verificadores de tipo estático como mypy para detectar erros antecipadamente. Os verificadores de tipo estático analisam seu código sem executá-lo e podem encontrar erros relacionados a tipos antes de você executar o código.
  4. Elas melhoram a legibilidade e a capacidade de manutenção do código. Quando você ou outros desenvolvedores retornarem ao código mais tarde, será mais fácil entender o que o código está fazendo.

Em uma base de código grande, esses benefícios podem reduzir significativamente os bugs e tornar o código mais fácil de entender e manter.

Observação: As dicas de tipo são opcionais no Python, mas são cada vez mais usadas em código profissional. Bibliotecas como as da biblioteca padrão do Python e muitos pacotes de terceiros populares agora incluem dicas de tipo extensivas.

Resumo

Neste laboratório, você aprendeu vários aspectos-chave do design de funções em Python. Primeiro, você aprendeu o design básico de funções, especificamente como escrever funções para processar dados CSV em várias estruturas de dados. Você também explorou a flexibilidade da função, refatorando funções para trabalhar com qualquer fonte iterável, aprimorando a versatilidade e a reutilização do código.

Além disso, você dominou a adição de parâmetros opcionais para lidar com diferentes casos de uso, como arquivos CSV com ou sem cabeçalhos, e o uso do sistema de dicas de tipo (type hinting) do Python para melhorar a legibilidade e a capacidade de manutenção do código. Essas habilidades são cruciais para escrever código Python robusto e, à medida que seus programas se tornam mais complexos, esses princípios de design manterão seu código organizado e compreensível. As técnicas podem ser aplicadas além do processamento CSV, tornando-as valiosas em seu kit de ferramentas de programação Python.