[python drops] NamedTuples


O módulo collections do Python é cheio de recursos bem úteis. Uma delas é a NamedTuples. Como o nome já diz, a ideia por trás é ter tuplas nomeadas. Ou seja: os mesmos recursos básicos de uma tupla mas com nomes para os atributos. Dessa forma, você não precisa se preocupar em memorizar a ordem dos elementos, apenas saber o nome deles. Esse recurso não é novo mas pode ser bem útil ter ele na manga.

Como utilizar então?

Para começar, você precisa importar namedtuple do módulo de collections. Em seguida, você irá criar praticamente uma classe inline. O primeiro parâmetro é o nome do seu objeto. O segundo, são os atributos, separados por espaço. Vale lembrar que palavras reservadas não são permitidas.

>>> from collections import namedtuple

>>> Trip = namedtuple('Trip', 'when where max_budget alone')

>>> # or Trip = namedtuple('Trip', ['when', 'where', 'max_budget', 'alone'])

>>> type(Trip)
<class 'type'>
>>> trip_to_italy = Trip(when='2020-09-10', where='Rome', max_budget=500, alone=False)

>>> # or trip_to_italy = Trip('2020-09-10', 'Rome', 500, False)

>>> trip_to_italy
Trip(when='2020-09-10', where='Rome', max_budget=500, alone=False)

>>> trip_to_italy.when
'2020-09-10'

>>> trip_to_italy.where
'Rome'

>>> trip_to_italy.max_budget
500

>>> trip_to_italy.alone
False

Pra fazer:

trip = Trip(when='2020-09-10', where='Rome', max_budget=500, alone=False), te custaria:

class Trip:

  def __init__(self, when, where, max_budget, alone):
    self.when = when
    self.where = where
    self.max_budget = max_budget
    self.alone = alone

E se você quiser converter para um dicionário, é bem fácil:

>>> trip_to_italy._asdict()

OrderedDict([('when', '2020-09-10'), ('where', 'Rome'), ('max_budget', 500), ('alone', False)])

Alguns métodos chamados de helpers estão disponíveis e começam com um underscore _. Exemplos: _make, para criar novas instâncias, e _replace, para criar uma cópia que substitui alguns valores.

Bom e quais as aplicações disso? Um dos usos, seria para eliminar o code smell Long Parameter List, onde você tem métodos com uma lista de parâmetros longa. Por exemplo:

def send_offer_by_email(offer, customer, email_from, subject):
	value = offer.value
	sales_agent = offer.sales_agent
	email_to = customer.email
	[...]

	message = f'Hello dear customer, you offer costs ${value}. If you have any questions, {sales_agent} is available to talk with you. Thank you!'

	notifications.by_email(subject, email_from, customer_email, message)

Claro que existem métodos bem maiores (e mais organizados) que esse. Mas digamos que existem outros lugares que envia a atualização do valor ou o preço da manutenção não apenas da oferta. Basicamente os dados seriam os mesmos.

Vamos tentar organizar os parâmetros de send_offer_by_email usando namedtuple.

CostsEmail = namedtuple('CostsEmail', 'value sales_agent email_to email_from subject'

costs_email = CostsEmail(
    value=offer.value,
    sales_agent=offer.sales_agent,
    to=customer.email,
    from=email_from,
    subject=subject,
)

Bem, considerando que o nosso objetivo é apenas salvar os dados de um email relacionado a custos, já podemos refatorar o nosso método:

def send_offer_by_email(costs_email):
	message = f'Hello dear customer, you offer costs ${costs_email.value}. If you have any questions, {costs_email.sales_agent} is available to talk with you. Thank you!'

	notifications.by_email(costs_email.subject, costs_email.from, costs_email.to, message)

Reduzimos algumas linhas e deixamos o código mais limpo. Talvez o exemplo não tenha ficado dos melhores mas eu acho que vocês pegaram a ideia! :)

Um uso que achei bacana foi para normalização de entradas pro banco de dados sem adicioná-las diretamente as escolhas.

Como uma tupla, vale lembrar que namedtuple também são imutáveis e acessíveis via index.

Até mais!


comments powered by Disqus