Pular para o conteúdo

Como usar named tuples no Python?

CodeMDD.io

Escrevendo Código Pythonico e Limpo com Named Tuples

A module collections do Python fornece uma função de fábrica chamada namedtuple(), que é especialmente projetada para tornar seu código mais Pythonico quando você está trabalhando com tuples. Com namedtuple(), você pode criar tipos de sequência imutáveis que permitem acessar seus valores usando nomes descritivos dos campos e a notação de ponto em vez de índices inteiros confusos.

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

Usando namedtuple para Escrever Código Pythonico

O namedtuple é uma função de fábrica que retorna uma nova classe do tipo tuple com campos nomeados. Isso permite que você acesse seus elementos usando uma notação de ponto mais legível em vez de índices inteiros.

Para criar uma classe namedtuple usando a função namedtuple(), você precisa especificar um nome para a classe e uma string contendo nomes de campos separados por vírgulas como argumentos. O exemplo a seguir ilustra como criar uma namedtuple chamada Point com os campos x e y:

from collections import namedtuple
Point = namedtuple('Point', ['x', 'y'])

Uma vez criado o namedtuple, você pode usar essa classe para criar instâncias e acessar seus elementos usando seus campos nomeados. Veja um exemplo:

p = Point(1, 2)
print(p.x) # Output: 1
print(p.y) # Output: 2

Criando Classes Semelhantes a Tuplas com namedtuple()

A função namedtuple() também pode ser usada para criar classes semelhantes a tuplas com campos opcionais. Quando você cria uma namedtuple com campos opcionais, você pode deixá-los de fora ao criar uma instância e eles terão um valor padrão definido. Para isso, você precisa adicionar um sinal de igual aos nomes dos campos e especificar um valor padrão.

Veja um exemplo de como criar uma namedtuple chamada Person com os campos name e age, sendo que o campo age é opcional e tem um valor padrão de None:

Person = namedtuple('Person', ['name', 'age='None'])

Dessa forma, você pode criar uma instância de Person apenas com o campo name e o campo age terá o valor padrão de None:

p = Person('Alice')
print(p.name) # Output: Alice
print(p.age) # Output: None

Você também pode especificar um valor diferente para o campo age ao criar a instância:

p = Person('Bob', age=30)
print(p.name) # Output: Bob
print(p.age) # Output: 30

Explorando Outras Funcionalidades das Classes namedtuple

Além do acesso aos elementos usando nomes de campo, as classes namedtuple também têm outras funcionalidades úteis.

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

Você pode criar instâncias de uma namedtuple passando um iterável como argumento, onde cada elemento do iterável é atribuído a um campo da namedtuple na mesma ordem em que aparecem. Veja um exemplo:

from collections import namedtuple
Person = namedtuple('Person', ['name', 'age', 'city'])
data = ['Alice', 25, 'New York']
p = Person(*data)
print(p.name) # Output: Alice
print(p.age) # Output: 25
print(p.city) # Output: New York

Convertendo Instâncias de namedtuple em Dicionários

Você pode converter uma instância de namedtuple em um dicionário usando o método _asdict(). Esse método retorna uma nova instância de OrderedDict que contém os campos da namedtuple como pares chave-valor. Veja um exemplo:

from collections import namedtuple
Person = namedtuple('Person', ['name', 'age', 'city'])
p = Person('Alice', 25, 'New York')
d = p._asdict()
print(d) # Output: OrderedDict([('name', 'Alice'), ('age', 25), ('city', 'New York')])

Substituindo Campos em Instâncias de namedtuple

Uma vez que as instâncias de namedtuple são imutáveis, você não pode modificar diretamente seus campos. No entanto, você pode usar o método _replace() para criar uma nova instância de namedtuple com campos substituídos. Veja um exemplo:

from collections import namedtuple
Person = namedtuple('Person', ['name', 'age'])
p = Person('Alice', 25)
p = p._replace(age=30)
print(p) # Output: Person(name='Alice', age=30)

Explorando Atributos Adicionais das Classes namedtuple

Além dos campos nomeados especificados, as classes namedtuple também têm outros atributos úteis. Alguns desses atributos incluem:

  • __doc__: uma string que descreve a namedtuple
  • _fields: uma tupla contendo os nomes dos campos em ordem
  • _source: o código-fonte da namedtuple

Veja um exemplo de como acessar esses atributos:

from collections import namedtuple
Person = namedtuple('Person', ['name', 'age'])
print(Person.__doc__) # Output: Person(name, age)
print(Person._fields) # Output: ('name', 'age')
print(Person._source) # Output: <string>, código-fonte da namedtuple

Escrevendo Código Pythonico com namedtuple

O uso de namedtuple pode tornar seu código mais legível e Pythonico, substituindo o acesso a elementos por índices inteiros usando nomes de campo descritivos. Isso torna mais fácil entender o que cada elemento representa sem a necessidade de comentários adicionais.

Usando Nomes dos Campos em Vez de Índices

Ao usar namedtuple, você pode acessar seus elementos usando os nomes dos campos em vez de índices inteiros. Isso deixa seu código mais legível e menos propenso a erros. Veja um exemplo comparando o acesso a elementos usando um namedtuple e uma tuple comum:

from collections import namedtuple
Person = namedtuple('Person', ['name', 'age'])
p = Person('Alice', 25)
# Usando namedtuple
print(p.name) # Output: Alice
print(p.age) # Output: 25
# Usando tuple
t = ('Alice', 25)
print(t[0]) # Output: Alice
print(t[1]) # Output: 25

Retornando Múltiplos Valores Nomeados de Funções

Uma das vantagens do uso de namedtuple é que você pode retornar múltiplos valores de uma função e atribuí-los a variáveis com nomes correspondentes. Isso torna o código mais legível e evita o uso de comentários adicionais para explicar o significado de cada valor retornado.

Veja um exemplo de como retornar múltiplos valores nomeados de uma função usando namedtuple:

from collections import namedtuple
def get_person():
return Person(name='Alice', age=25)
p = get_person()
print(p.name) # Output: Alice
print(p.age) # Output: 25

Reduzindo o Número de Argumentos de Funções

Quando você tem uma função que recebe muitos argumentos, pode usar uma namedtuple para agrupar esses argumentos em uma única estrutura de dados legível. Isso torna o código mais limpo e reduz a quantidade de argumentos que precisam ser passados e gerenciados em cada chamada de função.

Veja um exemplo de como reduzir o número de argumentos de função usando uma namedtuple:

from collections import namedtuple
Point = namedtuple('Point', ['x', 'y'])
def distance(p1, p2, precision=2):
return round(((p2.x - p1.x) ** 2 + (p2.y - p1.y) ** 2) ** 0.5, precision)
p1 = Point(0, 0)
p2 = Point(3, 4)
print(distance(p1, p2)) # Output: 5.0

Lendo Dados Tabulares de Arquivos e Bancos de Dados

Em muitos casos, você precisa lidar com dados tabulares, como CSVs, planilhas ou bancos de dados. O uso de uma namedtuple pode facilitar a leitura desses dados, atribuindo valores a campos nomeados e facilitando o tratamento do código.

Veja um exemplo de como ler dados tabulares de um arquivo CSV usando namedtuple:

from collections import namedtuple
import csv
Person = namedtuple('Person', ['name', 'age', 'city'])
with open('data.csv', newline='') as file:
reader = csv.reader(file)
next(reader) # Ignora o cabeçalho do CSV
for row in reader:
p = Person(*row)
print(p.name, p.age, p.city)

Usando namedtuple vs Outras Estruturas de Dados

namedtuple vs Dicionário

Uma das principais diferenças entre um namedtuple e um dicionário é que o namedtuple é uma estrutura de dados imutável, enquanto um dicionário é mutável. Isso significa que você não pode modificar os campos de uma namedtuple após sua criação, enquanto que em um dicionário é possível adicionar, remover ou atualizar elementos a qualquer momento.

No entanto, o uso de um namedtuple pode ser preferível ao usar um dicionário quando você tem dados que não precisam ser modificados. Além disso, o acesso aos campos de um namedtuple é mais rápido e eficiente do que o acesso aos elementos de um dicionário.

namedtuple vs Data Class

As data classes foram introduzidas no Python 3.7 como uma maneira de simplificar a criação de classes de objetos de dados. Elas fornecem automaticamente os métodos __init__(), __repr__(), __eq__(), etc., e permitem anotações de tipo.

Embora as data classes possam ter uma sintaxe mais fácil de usar e forneçam alguns recursos adicionais, o uso de um namedtuple pode ser preferível em algumas situações. O namedtuple é uma estrutura de dados mais leve e eficiente, ideal para casos em que você precisa criar muitas instâncias e acessar os campos de forma eficiente.

namedtuple vs typing.NamedTuple

O módulo typing do Python oferece uma maneira de criar named tuples usando a classe NamedTuple. Esta classe permite criar named tuples com anotações de tipo, o que pode melhorar a legibilidade e a facilidade de manutenção do código.

No entanto, a criação de named tuples usando a função namedtuple() ainda é amplamente utilizada e oferece uma sintaxe mais simples e concisa para criar named tuples sem anotações de tipo.

Subclassificando namedtuple

Você também pode criar subclasses de uma namedtuple para adicionar comportamentos e métodos personalizados. Para fazer isso, basta herdar da classe namedtuple e adicionar métodos e atributos adicionais conforme necessário.

Veja um exemplo de como criar uma subclasse de namedtuple chamada Point3D que adiciona um método para calcular a distância entre 3 pontos no espaço tridimensional:

from collections import namedtuple
Point3D = namedtuple('Point3D', ['x', 'y', 'z'])
class Point3D(Point3D):
def distance(self, other):
return ((other.x - self.x) ** 2 + (other.y - self.y) ** 2 + (other.z - self.z) ** 2) ** 0.5
p1 = Point3D(1, 2, 3)
p2 = Point3D(4, 5, 6)
print(p1.distance(p2)) # Output: 5.196152422706632

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

Tupla

Uma tupla é uma sequência imutável de elementos que pode conter qualquer tipo de dado. A criação de uma tupla é mais rápida do que a criação de uma lista de tamanho equivalente, pois as tuplas são imutáveis e não há necessidade de alocar memória adicional para armazenar elementos adicionais.

import timeit
creation_time = timeit.timeit('x = (1, 2, 3, 4, 5)', number=1000000)
print(f'Tempo de criação de uma tupla: {creation_time} segundos')

namedtuple

A criação de uma namedtuple é um pouco mais lenta do que a criação de uma tupla, devido a etapas adicionais necessárias para definir os campos nomeados. No entanto, a diferença de desempenho é insignificante para a maioria das aplicações.

import timeit
from collections import namedtuple
Person = namedtuple('Person', ['name', 'age', 'city'])
creation_time = timeit.timeit('p = Person("Alice", 25, "New York")', number=1000000)
print(f'Tempo de criação de uma namedtuple: {creation_time} segundos')

Conclusão

Neste tutorial, você aprendeu como usar as namedtuples do módulo collections para escrever código Pythonico e limpo. As namedtuples são uma forma conveniente de criar classes imutáveis com campos nomeados, o que facilita o acesso a elementos e melhora a legibilidade do código. Além disso, exploramos outras funcionalidades das namedtuples, como a criação de instâncias a partir de iteráveis, a conversão para dicionários e a substituição de campos em instâncias existentes. Também discutimos as vantagens do uso de namedtuples em comparação com outras estruturas de dados, como dicionários e data classes.

Com o conhecimento adquirido neste tutorial, você está pronto para utilizar as namedtuples em seus projetos Python e escrever código mais legível e eficiente.