Pular para o conteúdo

Pilhas nomeadas em Python: Como usar namedtuple facilmente?

[

Escrevendo um Código Pythonic e Limpo com namedtuple

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ável que permitem acessar seus valores usando nomes descritivos de campo e a notação de ponto em vez de índices inteiros incertos.

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

Neste tutorial, você aprenderá a:

  • Criar classes namedtuple usando namedtuple()
  • Identificar e aproveitar os recursos incríveis do namedtuple
  • Usar instâncias de namedtuple para escrever um código Pythonic
  • Decidir se o uso de uma namedtuple é apropriado ou se deve-se usar uma estrutura de dados similar
  • Subclassificar uma namedtuple para fornecer novos recursos

Se você não possui todo o conhecimento necessário antes de começar este tutorial, não se preocupe! Você pode parar e revisar os recursos mencionados acima, conforme necessário.

Usando namedtuple para Escrever um Código Pythonic

Uma namedtuple é uma fábrica de tipos de dados que cria subclasses de tuple com campos nomeados. Ela é definida usando dois argumentos: um nome para a classe namedtuple e uma string contendo os nomes dos campos.

Veja um exemplo abaixo:

from collections import namedtuple
# Criando uma namedtuple chamada 'Point'
Point = namedtuple('Point', ['x', 'y'])
# Criando uma instância da namedtuple 'Point'
p = Point(1, 2)
# Acessando os campos da instância
print(p.x) # Output: 1
print(p.y) # Output: 2

Neste exemplo, criamos uma namedtuple chamada Point com os campos x e y. Em seguida, criamos uma instância dessa namedtuple com os valores 1 e 2. Em seguida, podemos acessar os campos da instância usando a notação de ponto.

Criando Classes Semelhantes a Tuplas com namedtuple()

A função namedtuple() também aceita um argumento adicional opcional, que é uma string com os nomes dos campos separados por espaço, em vez de uma lista ou tupla.

Veja um exemplo abaixo:

from collections import namedtuple
# Criando uma namedtuple chamada 'Circle' usando uma string com campos separados por espaço
Circle = namedtuple('Circle', 'center_x center_y radius')
# Criando uma instância da namedtuple 'Circle'
c = Circle(0, 0, 5)
# Acessando os campos da instância
print(c.center_x) # Output: 0
print(c.center_y) # Output: 0
print(c.radius) # Output: 5

Neste exemplo, criamos uma namedtuple chamada Circle usando uma string com os nomes dos campos separados por espaço. Podemos criar uma instância dessa namedtuple da mesma forma que antes e acessar os campos usando a notação de ponto.

Explorando Recursos Adicionais das Classes namedtuple

As classes namedtuple têm outros recursos interessantes além da capacidade de criar instâncias com campos nomeados. Vejamos alguns desses recursos.

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

Você pode criar instâncias de namedtuple usando iteráveis em vez de passar todos os valores como argumentos separados. Os elementos do iterável são atribuídos aos campos da namedtuple na ordem em que são fornecidos.

Veja um exemplo abaixo:

from collections import namedtuple
# Criando uma namedtuple chamada 'Color' com os campos 'r', 'g' e 'b'
Color = namedtuple('Color', ['r', 'g', 'b'])
# Criando uma lista com os valores dos campos
color_values = [255, 0, 0]
# Criando uma instância da namedtuple 'Color' usando os valores da lista
c = Color._make(color_values)
# Acessando os campos da instância
print(c.r) # Output: 255
print(c.g) # Output: 0
print(c.b) # Output: 0

Neste exemplo, criamos uma instância da namedtuple Color usando a função estática _make() e passando uma lista com os valores dos campos. Os elementos da lista são atribuídos aos campos na ordem especificada.

Convertendo Instâncias de namedtuple em Dicionários

Você pode converter instâncias de namedtuple em dicionários usando o método _asdict(). Isso retorna um objeto OrderedDict com os nomes dos campos como chaves e os respectivos valores das instâncias como valores.

Veja um exemplo abaixo:

from collections import namedtuple
# Criando uma namedtuple chamada 'Person' com os campos 'name', 'age' e 'city'
Person = namedtuple('Person', ['name', 'age', 'city'])
# Criando uma instância da namedtuple 'Person'
p = Person('John Doe', 30, 'New York')
# Convertendo a instância em um dicionário
p_dict = p._asdict()
# Imprimindo o dicionário
print(p_dict)

Neste exemplo, criamos uma instância da namedtuple Person e depois a convertemos em um dicionário usando o método _asdict(). O dicionário resultante p_dict contém os nomes dos campos como chaves e os respectivos valores das instâncias como valores.

Substituindo Campos em Instâncias Existentes de namedtuple

Você pode substituir campos específicos em instâncias existentes de namedtuple usando o método _replace(). O método retorna uma nova instância da namedtuple com os campos substituídos pelos novos valores especificados.

Veja um exemplo abaixo:

from collections import namedtuple
# Criando uma namedtuple chamada 'Person' com os campos 'name' e 'age'
Person = namedtuple('Person', ['name', 'age'])
# Criando uma instância da namedtuple 'Person'
p = Person('John Doe', 30)
# Substituindo o campo 'age' na instância por um novo valor
p_new = p._replace(age=35)
# Acessando os campos da nova instância
print(p_new.name) # Output: John Doe
print(p_new.age) # Output: 35

Neste exemplo, criamos uma instância da namedtuple Person chamada p e depois a substituímos pelo campo age usando o método _replace(). O novo valor do campo é fornecido como argumento no formato campo=novo_valor. A nova instância p_new tem o campo age substituído pelo novo valor fornecido.

Explorando Atributos Adicionais das namedtuple Classes

As classes namedtuple também têm outros atributos adicionais úteis que podem ser acessados. Alguns desses atributos são:

  • ._fields: Retorna uma tupla com os nomes dos campos da namedtuple.
  • ._make(iterable): Cria uma nova instância da namedtuple usando o iterável fornecido.
  • ._asdict(): Converte a instância da namedtuple em um objeto OrderedDict.
  • ._replace(**kwargs): Cria uma nova instância da namedtuple, substituindo os campos fornecidos pelos novos valores.

Escrevendo um Código Pythonic com namedtuple

A principal vantagem de usar namedtuple em vez de listas ou tuplas é a capacidade de acessar os valores pelos nomes dos campos, em vez de usar índices inteiros. Isso torna o código mais legível e menos propenso a erros.

Veja um exemplo abaixo:

from collections import namedtuple
# Criando uma namedtuple chamada 'Book' com os campos 'title', 'author' e 'year'
Book = namedtuple('Book', ['title', 'author', 'year'])
# Criando uma função para retornar várias instâncias de namedtuple
def get_books():
b1 = Book('Python Tricks', 'Dan Bader', 2017)
b2 = Book('Fluent Python', 'Luciano Ramalho', 2015)
b3 = Book('Clean Code', 'Robert C. Martin', 2008)
return b1, b2, b3
# Chamando a função e atribuindo as instâncias de namedtuple a variáveis
book1, book2, book3 = get_books()
# Imprimindo os valores dos campos usando a notação de ponto
print(book1.title, book1.author, book1.year)
print(book2.title, book2.author, book2.year)
print(book3.title, book3.author, book3.year)

Neste exemplo, criamos uma função get_books() que retorna várias instâncias da namedtuple Book. Em seguida, chamamos essa função e atribuímos as instâncias retornadas a variáveis individuais. Por fim, usamos a notação de ponto para acessar os valores dos campos e imprimi-los.

Comparando namedtuple com Outras Estruturas de Dados

Durante o desenvolvimento de seu código, você pode se deparar com várias opções de estruturas de dados para armazenar e manipular seus dados. Aqui, vamos comparar o uso de namedtuple com outras estruturas de dados comumente usadas no Python.

namedtuple vs Dicionário

Os dicionários são estruturas de dados muito flexíveis, pois permitem armazenar pares chave-valor de qualquer tipo, incluindo outros objetos. Eles são ideais quando você precisa associar um valor a um rótulo.

No entanto, ao usar um dicionário, você precisa se lembrar dos nomes das chaves e escrevê-los corretamente sempre que quiser acessar um valor. Isso pode ser propenso a erros e mais difícil de ler, especialmente quando se lida com várias instâncias que têm o mesmo conjunto de campos.

Por outro lado, com namedtuple, você pode ter os nomes dos campos como atributos de instância e acessá-los diretamente usando a notação de ponto, o que resulta em um código mais limpo e legível.

namedtuple vs Data Class

As data classes são um recurso introduzido no Python 3.7 que simplificam a criação de classes de dados. Elas são úteis quando você precisa implementar métodos personalizados, mas não oferecem recursos avançados de controle de tipos.

As namedtuple são mais simples de definir e não exigem a importação de um módulo adicional, pois estão disponíveis no módulo collections do Python. Elas são ideais para casos em que você deseja apenas armazenar dados e acessá-los pelos nomes dos campos, sem a necessidade de adicionar funcionalidades adicionais às instâncias.

namedtuple vs typing.NamedTuple

O módulo typing do Python introduziu a classe NamedTuple, que é uma classe genérica que permite definir namedtuples com tipos de dados especificados. Essa classe é útil quando você precisa fazer anotações de tipo e especificar tipos de dados para os campos da namedtuple.

No entanto, ao usar typing.NamedTuple, é necessário importar o módulo typing e definir explicitamente os tipos de dados dos campos da namedtuple, o que pode levar a um código mais verboso. Por outro lado, namedtuple do módulo collections é mais conciso e mantém a legibilidade do código.

Subclassificando Classes namedtuple

Assim como classes regulares, namedtuples podem ser subclassificadas para adicionar novos campos ou funcionalidades adicionais. Isso é útil quando você deseja personalizar o comportamento das instâncias de namedtuple para atender a requisitos específicos.

Veja um exemplo abaixo:

from collections import namedtuple
# Criando uma namedtuple chamada 'Person' com os campos 'name' e 'age'
Person = namedtuple('Person', ['name', 'age'])
# Subclassificando a namedtuple 'Person' para adicionar o campo 'city'
class NewPerson(Person):
def __new__(cls, name, age, city):
return super().__new__(cls, name, age)
def __init__(self, name, age, city):
super().__init__(name, age)
self.city = city
# Criando uma instância da nova namedtuple 'NewPerson'
p = NewPerson('John Doe', 30, 'New York')
# Acessando os campos da instância
print(p.name) # Output: John Doe
print(p.age) # Output: 30
print(p.city) # Output: New York

Neste exemplo, criamos a namedtuple Person com os campos name e age. Em seguida, subclassificamos essa namedtuple para adicionar o campo city. Sobrescrevemos o método __new__() para garantir que o campo city seja passado corretamente quando uma nova instância for criada. Também adicionamos o método __init__() para inicializar o novo campo. Em seguida, criamos uma instância da nova namedtuple NewPerson e podemos acessar os campos usando a notação de ponto.

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

Ao criar muitos objetos, o tempo de criação pode ser um fator importante a ser considerado. Vamos comparar o tempo de criação de tuples e namedtuples para ver a diferença.

Veja um exemplo abaixo:

from collections import namedtuple
import timeit
# Definindo um tuple com campos 'a', 'b' e 'c'
tuple_fields = ('a', 'b', 'c')
# Função para criar um tuple
def create_tuple():
return tuple(range(3))
# Função para criar uma namedtuple
def create_namedtuple():
Point = namedtuple('Point', tuple_fields)
return Point(0, 1, 2)
# Medindo o tempo de criação de 100000 tuples
time_tuple = timeit.timeit(create_tuple, number=100000)
# Medindo o tempo de criação de 100000 namedtuples
time_namedtuple = timeit.timeit(create_namedtuple, number=100000)
# Imprimindo os tempos de criação
print(f"Tempo de criação do tuple: {time_tuple}")
print(f"Tempo de criação do namedtuple: {time_namedtuple}")

Neste exemplo, usamos a função timeit() do módulo timeit para medir o tempo de criação de 100.000 tuples e 100.000 namedtuples. Em seguida, imprimimos os tempos de criação medidos.

No geral, namedtuples tendem a ter um tempo de criação um pouco mais longo em comparação com tuples. No entanto, a diferença de desempenho pode ser negligenciada na maioria dos casos, a menos que você esteja criando um grande número de objetos. Nesses casos, pode ser necessário fazer mais testes para determinar a melhor escolha.

Conclusão

namedtuple é uma ferramenta útil no Python que facilita a criação de código Pythonic e limpo. Com a capacidade de acessar valores através de nomes de campo descritivos e a notação de ponto, namedtuple torna o código mais legível e menos propenso a erros.

Neste tutorial, você aprendeu a criar namedtuple usando a função namedtuple(), bem como a explorar recursos adicionais, como criar instâncias a partir de iteráveis, converter instâncias em dicionários, substituir campos em instâncias existentes e acessar atributos adicionais de namedtuple classes.

Você também comparou namedtuple com outras estruturas de dados comumente usadas, como dicionários, data classes e typing.NamedTuple. Além disso, você aprendeu a subclassificar namedtuple para adicionar funcionalidades adicionais e medir o tempo de criação de namedtuples em comparação com tuples.

Com o conhecimento adquirido neste tutorial, você poderá usar namedtuple para escrever um código mais Pythonic, limpo e legível em seus projetos Python.

Remove ads