Crie classes sem esforço em Python com @dataclass

Jan 22 2023
Em Python, o decorador @dataclass simplifica o processo de criação de classes adicionando automaticamente métodos comuns e promovendo as melhores práticas para escrever um código seguro e bem escrito. Está disponível no Python 3.
Imagem gerada por Jacob Ferus

Em Python, o @dataclassdecorador simplifica o processo de criação de classes adicionando automaticamente métodos comuns e promovendo as melhores práticas para escrever um código seguro e bem escrito. Está disponível no Python 3.7. Neste artigo, vou contar tudo sobre esse decorador.

Vamos começar com um exemplo simples:

Definindo duas variáveis ​​estáticas com dicas de tipo e adicionando o @dataclassdecorador, obtemos uma classe com um __init__método correspondente ao seguinte:

Embora pular o __init__método - seja bom, a classe de dados oferece muito mais do que apenas isso. Para entender o que, vamos dar uma olhada nas opções disponíveis para o decorador:

Omiti alguns dos argumentos e mantive aqueles que considero mais importantes para discutir. Note que usando:

resultará equivalentemente no uso dos argumentos padrão.

Como pode ser visto, dependendo dos argumentos, os métodos serão adicionados implicitamente à classe. Por padrão:

  • init=True irá adicionar __init__como vimos acima
  • repr=True irá adicionar __repr__, ou seja, o que é mostrado ao imprimir
  • eq=True irá adicionar __eq__com base nos campos
  • order=True adicionará __lt__(), __le__(), __gt__()e__ge__()

Observe que você também pode definir outros métodos de classe personalizados, como faria normalmente em uma classe. Abaixo defino uma classe simples sem o decorador @dataclass para comparação.

Impressão

Com repr=True, que é definido por padrão, a saída durante a impressão é alterada. Vamos comparar o que o objeto feito com a classe data e o objeto feito com a classe normal (sem __repr__) estão mostrando quando impressos:

Como pode ser visto com a classe normal, as informações impressas nos dizem muito pouco sobre o objeto por padrão. Por outro lado, a classe de dados mostra os valores do objeto sucintamente, substituindo o __repr__método padrão.

Igualdade

O comportamento padrão de igualdade para classes personalizadas pode ser confuso. Se você comparar uma classe personalizada sem um __eq__-método definido, dois objetos serão iguais apenas se forem exatamente o mesmo objeto:

Assim, os valores dos atributos não importam. Mas usando o dataclass e eq=True(definido por padrão) a forma mais esperada de igualdade é adicionada onde os atributos são comparados (ou seja, __eq__é substituído):

Ordem

Definindo o orderparâmetro como true (false por padrão) e eq true ( other=Truee eq=Falsecausará um erro a ser lançado) os métodos:

  • __lt__()
  • __le__()
  • __gt__()
  • __ge__()

Imutabilidade e hashing

Finalmente, vamos falar sobre os argumentos frozene unsafe_hash. Esses argumentos são muito úteis e facilitam o gerenciamento correto de hashing, igualdade e mutação. Se você não está familiarizado com hash em Python, recomendo a leitura do artigo: Understanding Hashing and Equality in Python with __hash__ and __eq__ .

A configuração frozen=Trueafirma que a classe deve ser somente leitura ou imutável, ou seja, após a inicialização de um objeto, nenhum campo pode ser alterado:

As duas propriedades frozenafetarão eqo método de hashing implícito que é criado:

  • frozen=True, eq=True=> um __hash__-método é gerado automaticamente usando os campos
  • frozen=True, eq=False=> default __hash__-method, ou seja, não usando os campos
  • frozen=False, eq=True=> Unhashable, erro é lançado se hash
  • frozen=False, eq=False=> default __hash__-method, ou seja, não usando os campos

Mas por que a classe tem que ser congelada? Se você adicionar um objeto a um conjunto ou dicionário, o hash gerado será usado para colocá-lo dentro do mapa de hash. Posteriormente, quando quiser recuperar o objeto, use o mesmo hash para localizá-lo. Agora, se o objeto foi alterado, o hash também deve mudar (já que é baseado nos campos). Consequentemente, você não poderá encontrar o objeto novamente. Assim, para que o hash permaneça constante, os campos nos quais ele se baseia devem permanecer constantes. Por esse motivo, if eq=Truee frozen=False, não há nenhum método de hash disponível.

E sobre unsafe_hash? Conforme descrito, o comportamento predefinido da definição implícita de __hash__é baseado em princípios que promovem as melhores práticas. No entanto, se você ainda deseja ter um __hash__método -definido de qualquer maneira, talvez devido a circunstâncias especiais, você pode definir unsafe_hash=Truee isso será feito. Mas esteja ciente de que a palavra-chave unsafe existe por um motivo:

Conclusão

O @dataclass em Python é um decorador útil que nos permite economizar tempo, seguir as melhores práticas e simplificar o processo de criação de classes. Eu acredito que é adequado para uma ampla variedade de situações.

Obrigado por ler!

Você também pode estar interessado em: