Pular para o conteúdo

Como usar namedtuple em Python?

CodeMDD.io

Escrevendo Código Pythonic e Limpo com named tuple

O módulo collections do Python fornece uma função de fábrica chamada namedtuple(), que é especialmente projetada para tornar seu código mais Pythonic quando você está trabalhando com tuplas. Com namedtuple(), você pode criar tipos de sequência imutáveis que permitem que você acesse seus valores usando nomes descritivos de campo e a notação de ponto, em vez de índices inteiros.

Se você tem alguma experiência usando Python, então sabe que escrever código Pythonic é uma habilidade essencial para desenvolvedores Python. Neste tutorial, você aprimorará essa habilidade usando namedtuple.

Neste tutorial, você aprenderá:

  • Como criar classes namedtuple usando o namedtuple()
  • Identificar e aproveitar recursos interessantes do namedtuple
  • Usar instâncias de namedtuple para escrever código Pythonic
  • Decidir se deve usar uma namedtuple ou uma estrutura de dados semelhante
  • Fazer a subclasse de uma namedtuple para adicionar novos recursos

Caso não possua todo o conhecimento necessário antes de iniciar este tutorial, não se preocupe! Você pode interromper e revisar os recursos mencionados conforme necessário.

Usando namedtuple para Escrever Código Pythonic

Antes de começarmos a trabalhar com a função namedtuple(), vamos entender por que ela é importante para escrever código Pythonic. Em Python, os desenvolvedores são encorajados a escrever código que seja legível e expressivo. Isso é conhecido como “código Pythonic”. O uso de índices inteiros para acessar elementos em uma tupla pode dificultar a leitura e entendimento do código.

Ao usar o namedtuple(), você pode definir nomes descritivos para os campos de uma tupla, o que torna o código mais legível e expressivo. Além disso, as instâncias de namedtuple são imutáveis, o que significa que elas não podem ser alteradas após a criação. Isso pode ser útil em situações em que você deseja garantir que os valores não sejam alterados acidentalmente.

Criando Classes Semelhantes a Tuplas com namedtuple()

A função namedtuple() permite que você crie novas classes que são semelhantes a tuplas, mas com nomes descritivos para os campos. Vamos ver como isso funciona na prática.

Fornecendo Argumentos Obrigatórios para namedtuple()

Você pode usar o namedtuple() para criar uma nova classe semelhante a uma tupla, fornecendo os nomes dos campos como uma string separada por espaços, juntamente com o nome da classe. Veja o exemplo abaixo:

from collections import namedtuple
Car = namedtuple('Car', 'brand model year')
car1 = Car('Toyota', 'Camry', 2020)
car2 = Car('Honda', 'Civic', 2019)
print(car1.brand) # Output: Toyota
print(car2.model) # Output: Civic

Neste exemplo, criamos uma classe Car utilizando namedtuple(). Definimos três campos para essa classe: brand, model e year. Em seguida, criamos duas instâncias dessa classe, car1 e car2, passando os valores para cada campo.

Agora podemos acessar esses valores utilizando o nome dos campos como atributos do objeto. Isso torna o código mais legível e menos sujeito a erros.

Usando Argumentos Opcionais com namedtuple()

Além dos argumentos obrigatórios, você pode usar argumentos opcionais com a função namedtuple(). Esses argumentos permitem personalizar ainda mais as classes namedtuple criadas.

Um dos argumentos opcionais mais comumente utilizados é o defaults, que define valores padrão para os campos da classe namedtuple. Vamos ver um exemplo:

from collections import namedtuple
Person = namedtuple('Person', 'name age', defaults=['', 0])
person1 = Person()
person2 = Person('John', 30)
print(person1) # Output: Person(name='', age=0)
print(person2) # Output: Person(name='John', age=30)

Neste exemplo, criamos uma classe Person utilizando namedtuple() com os campos name e age. Passamos um argumento opcional defaults=['', 0], que define os valores padrão para cada campo como uma string vazia e zero, respectivamente.

Quando criamos person1 e person2 sem passar valores para os campos, eles assumem os valores padrão definidos em defaults. Isso pode ser útil quando você quiser ter valores padrão para os campos, especialmente quando não tiver todos os valores disponíveis no momento da criação.

Explorando Recursos Adicionais das Classes namedtuple

O namedtuple tem outros recursos interessantes que podem facilitar seu trabalho ao lidar com dados. Vamos explorar alguns deles a seguir.

Criando Instâncias de namedtuple a Partir de Iteráveis

Uma das vantagens do namedtuple é que você pode criar instâncias dessa classe a partir de iteráveis, como listas ou tuplas. Vamos ver como isso funciona:

from collections import namedtuple
Point = namedtuple('Point', 'x y')
data = [10, 20]
point = Point._make(data)
print(point) # Output: Point(x=10, y=20)

Neste exemplo, temos uma lista chamada data com os valores [10, 20]. Em seguida, utilizamos o método _make de namedtuple para criar uma nova instância de Point a partir dessa lista.

Ao imprimir point, vemos que os valores x e y são preenchidos corretamente com os valores da lista data. Isso permite que você inicialize instâncias namedtuple com dados existentes de forma concisa.

Convertendo Instâncias de namedtuple em Dicionários

Outro recurso interessante do namedtuple é a capacidade de converter instâncias dessa classe em dicionários. Isso pode ser útil quando você precisa manipular os dados como um dicionário em algum ponto do seu código. Veja o exemplo abaixo:

from collections import namedtuple
Person = namedtuple('Person', 'name age')
person = Person('John', 30)
person_dict = person._asdict()
print(person_dict) # Output: {'name': 'John', 'age': 30}

Neste exemplo, criamos uma classe Person com os campos name e age utilizando namedtuple(). Em seguida, criamos uma instância chamada person com os valores 'John' e 30.

Em seguida, utilizamos o método _asdict() para converter essa instância em um dicionário chamado person_dict. O resultado é um dicionário com os campos name e age e seus respectivos valores.

Substituindo Campos em Instâncias Existentes de namedtuple

Você também pode substituir campos específicos em instâncias existentes de namedtuple utilizando o método _replace(). Veja o exemplo abaixo:

from collections import namedtuple
Person = namedtuple('Person', 'name age')
person = Person('John', 30)
new_person = person._replace(age=35)
print(new_person) # Output: Person(name='John', age=35)

Neste exemplo, criamos uma instância person da classe Person com os valores 'John' e 30. Em seguida, utilizamos o método _replace() para substituir o valor do campo age para 35. O resultado é uma nova instância chamada new_person com o campo age atualizado.

Esse recurso pode ser útil quando você precisa atualizar apenas um campo específico de uma instância existente de namedtuple, sem criar uma nova instância do zero.

Explorando Atributos Adicionais de namedtuple

As instâncias de namedtuple também possuem alguns atributos adicionais que podem ser úteis em determinadas situações.

O atributo ._fields retorna uma tupla com os nomes dos campos da classe namedtuple. Veja o exemplo:

from collections import namedtuple
Person = namedtuple('Person', 'name age')
print(Person._fields) # Output: ('name', 'age')

Neste exemplo, imprimimos o valor de Person._fields e obtemos a tupla ('name', 'age'), que são os nomes dos campos da classe Person.

Outro atributo útil é ._source, que retorna a string que define a classe namedtuple. Isso pode ser útil para fins de depuração. Veja o exemplo:

from collections import namedtuple
Person = namedtuple('Person', 'name age')
print(Person._source) # Output: "Person = namedtuple('Person', 'name age')"

Neste exemplo, imprimimos o valor de Person._source e obtemos a string "Person = namedtuple('Person', 'name age')", que é a definição da classe Person.

Escrevendo Código Pythonic com namedtuple

Agora que você aprendeu como criar e manipular instâncias de namedtuple, vamos explorar como você pode usar essas classes para escrever código Pythonic em diferentes situações.

Usando Nomes de Campos em Vez de Índices

Uma das vantagens de namedtuple é que você pode acessar os valores utilizando nomes descritivos de campo em vez de índices inteiros. Veja o exemplo:

from collections import namedtuple
Car = namedtuple('Car', 'brand model year')
car = Car('Toyota', 'Camry', 2020)
print(car.brand) # Output: Toyota
print(car.model) # Output: Camry
print(car.year) # Output: 2020

Neste exemplo, criamos uma classe Car utilizando namedtuple() com os campos brand, model e year. Ao criar uma instância dessa classe, podemos acessar os valores de cada campo utilizando o nome do campo como um atributo do objeto.

Este método de acesso a valores é mais legível e menos propenso a erros do que o uso de índices inteiros.

Retornando Múltiplos Valores Nomeados de Funções

Outra forma de usar namedtuple é retornar múltiplos valores nomeados de uma função. Veja o exemplo:

from collections import namedtuple
def get_person():
Person = namedtuple('Person', 'name age')
return Person('John', 30)
person = get_person()
print(person.name) # Output: John
print(person.age) # Output: 30

Neste exemplo, criamos uma função get_person() que retorna uma instância de Person criada com namedtuple. Ao chamar essa função e atribuir o valor retornado a person, podemos acessar os valores de name e age utilizando os nomes dos campos.

Isso permite que você retorne múltiplos valores de uma função e os utilize de forma mais legível e expressiva em outros trechos do código.

Reduzindo a Quantidade de Argumentos em Funções

namedtuple também pode ser usado para reduzir a quantidade de argumentos em funções. Em vez de passar uma lista ou uma tupla como argumento, você pode passar uma instância de namedtuple. Veja o exemplo:

from collections import namedtuple
Person = namedtuple('Person', 'name age')
def print_person(person):
print(f"Name: {person.name}")
print(f"Age: {person.age}")
person = Person('John', 30)
print_person(person)

Neste exemplo, temos uma função print_person() que recebe uma instância de Person como argumento. Dentro da função, podemos acessar os valores dos campos da instância person diretamente usando pontos.

Isso torna o código mais legível e menos propenso a erros, pois você não precisa se lembrar da ordem dos argumentos ao chamar a função.

Lendo Dados Tabulares de Arquivos e Bancos de Dados

O namedtuple também pode ser usado para ler dados tabulares de arquivos e bancos de dados de forma mais legível. Veja o exemplo abaixo:

from collections import namedtuple
Person = namedtuple('Person', 'name age')
people = []
with open('data.csv') as file:
for line in file:
values = line.strip().split(',')
person = Person(*values)
people.append(person)
for person in people:
print(person.name, person.age)

Neste exemplo, temos um arquivo CSV chamado data.csv que contém dados de pessoas. Utilizamos a função open() para abrir o arquivo em modo de leitura e percorremos cada linha com um loop for.

Dentro do loop, usamos o método strip() para remover os caracteres de quebra de linha e o método split(',') para dividir a linha em uma lista de valores. Em seguida, passamos essa lista como argumento para criar uma nova instância de Person utilizando namedtuple.

Finalmente, adicionamos a instância person à lista people e, em seguida, imprimimos os valores de name e age de cada pessoa.

Esse método permite ler os dados do arquivo de forma mais legível, evitando o uso de índices inteiros para acessar os valores.

Utilizando namedtuple vs Outras Estruturas de Dados

Ao decidir se deve usar namedtuple ou outra estrutura de dados, é importante considerar as necessidades específicas do seu código.

namedtuple vs Dicionário

namedtuple e dicionários compartilham algumas semelhanças, mas também têm diferenças importantes.

namedtuple é uma boa opção quando você precisar de uma estrutura de dados imutável com campos definidos e acesso aos valores usando nomes descritivos. Funciona bem em situações em que você sabe de antemão quais campos a tupla irá conter e não precisa alterar os seus valores após a criação.

Por outro lado, os dicionários são uma boa opção quando você precisa de uma estrutura de dados mutável e flexível, que pode crescer dinamicamente. Os dicionários permitem adicionar, remover e alterar os valores dos campos de forma rápida e eficiente.

Portanto, ao escolher entre namedtuple e dicionários, você deve considerar se precisa de uma estrutura de dados imutável ou mutável e se conhece antecipadamente os campos e valores que estarão contidos na estrutura.

namedtuple vs Data Class

Em versões recentes do Python (a partir do Python 3.7), também é possível usar data classes para criar classes semelhantes a tuplas com nomes descritivos de campo.

As data classes são uma opção mais flexível do que namedtuple, pois permitem adicionar métodos, herdar de outras classes e fazer uso de outras funcionalidades da orientação a objetos.

No entanto, namedtuple pode ser uma opção mais adequada quando você precisa apenas de uma estrutura de dados simples e eficiente para armazenar valores de campos, sem a necessidade de funcionalidades extras. namedtuple é mais leve e consome menos recursos, o que pode ser uma vantagem em determinados contextos.

Portanto, a escolha entre namedtuple e data classes depende da complexidade e das necessidades específicas do seu código.

namedtuple vs typing.NamedTuple

O módulo typing do Python fornece a classe NamedTuple que pode ser usada para criar classes semelhantes a tuplas com nomes descritivos de campo. No entanto, a implementação da NamedTuple é um pouco diferente da namedtuple do módulo collections.

A NamedTuple permite mais flexibilidade em relação aos tipos dos campos e pode ser uma escolha melhor quando você precisa garantir que os campos tenham tipos específicos ou quando precisa de funcionalidades extras da classe como herança ou polimorfismo.

No entanto, a namedtuple do módulo collections é mais leve e oferece uma sintaxe mais concisa para criar classes semelhantes a tuplas, o que pode ser uma escolha melhor em alguns casos.

Novamente, a escolha entre namedtuple e NamedTuple depende das necessidades específicas do seu código e do balanceamento entre recursos e flexibilidade.

Fazendo a Subclasse de Classes namedtuple

Uma vantagem interessante de utilizar namedtuple é a facilidade de fazer a subclasse de classes criadas com essa função. Isso permite adicionar novos recursos e funcionalidades às subclasses de namedtuple.

Vamos ver um exemplo de como fazer a subclasse de uma classe namedtuple:

from collections import namedtuple
Person = namedtuple('Person', 'name age')
class Employee(Person):
def __init__(self, name, age, department):
super().__init__(name, age)
self.department = department
employee = Employee('John', 30, 'Sales')
print(employee.name) # Output: John
print(employee.age) # Output: 30
print(employee.department) # Output: Sales

Neste exemplo, criamos uma classe Employee que é subclasse de Person, que foi criada com namedtuple. A classe Employee tem um campo extra chamado department, que representaria o departamento em que o funcionário trabalha.

No construtor da classe Employee, chamamos o construtor da classe Person utilizando super() para passar os valores de name e age. Em seguida, atribuímos o valor de department ao campo correspondente da classe Employee.

Dessa forma, podemos criar instâncias de Employee com os valores de name, age e department e acessar esses valores utilizando os nomes dos campos.

Medindo o Tempo de Criação: tuple vs namedtuple

Uma pergunta comum que pode surgir é se o uso de namedtuple afeta o desempenho do código em comparação com o uso de tuplas regulares.

Vamos fazer um teste simples para comparar o tempo de criação de tuplas regulares com o tempo de criação de namedtuple. Veja o exemplo abaixo:

import time
from collections import namedtuple
Person = namedtuple('Person', 'name age')
start_time = time.time()
for _ in range(1000000):
person = ('John', 30)
end_time = time.time()
print(f'Time elapsed for tuples: {end_time - start_time} seconds')
start_time = time.time()
for _ in range(1000000):
person = Person('John', 30)
end_time = time.time()
print(f'Time elapsed for namedtuples: {end_time - start_time} seconds')

Neste exemplo, utilizamos o módulo time para medir o tempo de criação de tuplas regulares e namedtuple. Criamos uma tupla person com os valores 'John' e 30 utilizando uma estrutura de repetição que se repete um milhão de vezes.

Depois de terminar a repetição, imprimimos o tempo decorrido para as tuplas regulares e, em seguida, adicionamos um intervalo de tempo semelhante para a criação de namedtuple.

Ao executar esse código, você obterá os tempos de execução para as duas abordagens. Os resultados variarão dependendo das especificações da sua máquina, mas é provável que você observe que o tempo de criação de namedtuple é ligeiramente mais lento do que o tempo de criação de tuplas regulares.

Essa diferença é mínima e geralmente não é significativa na maioria dos casos. Portanto, a menos que a velocidade de criação de tuplas seja um fator crítico de desempenho no seu código, a diferença de velocidade entre tuplas regulares e namedtuple pode ser considerada insignificante.

Conclusão

Neste tutorial, você aprendeu como usar namedtuple para escrever código Pythonic e limpo. Vimos como criar classes semelhantes a tuplas com nomes descritivos de campo, como utilizar recursos adicionais de namedtuple e como escrever código Pythonic em diferentes situações.

Lembrando que o namedtuple é especialmente útil quando você quer que seus campos sejam imutáveis e acessíveis através de nomes descritivos em vez de índices inteiros. Ele oferece uma sintaxe concisa e expressiva para trabalhar com tuplas imutáveis.

Ao usar namedtuple, é importante lembrar que essas instâncias são imutáveis e que você não pode alterar os valores de seus campos depois de criá-las. Isso faz com que namedtuple seja adequado para situações em que você precisa garantir que seus dados não sejam alterados acidentalmente.

No geral, namedtuple é uma ferramenta poderosa para escrever código Pythonic e limpo em situações em que você trabalha com tuplas. Espero que você possa aplicar esses conhecimentos em seu próprio código e aproveitar os benefícios do namedtuple na prática.