Módulo Dataclasses em Python
O módulo dataclasses
fornece o decorador dataclass
, que permite criar data classes, as quais reduzem significativamente o código repetitivo (boilerplate) das classes. Geralmente, tais classes são destinadas a armazenar algum estado ou dados, quando não é necessário algum comportamento na forma de funções.
Vamos considerar um exemplo simples:
class Person:
def __init__(self, name, age):
self.name = name
self.age = age
tom = Person("Tom", 38)
print(f"Name: {tom.name} Age: {tom.age}") # Name: Tom Age: 38
Aqui, definimos a classe Person
, na qual, na função construtora __init__
, são estabelecidos dois atributos: name
e age
. Em seguida, criamos um objeto dessa classe e exibimos os valores de seus atributos no console.
Agora, vamos modificar este programa, transformando a classe Person
em uma data class:
from dataclasses import dataclass
@dataclass
class Person:
name: str
age: int
tom = Person("Tom", 38)
print(f"Name: {tom.name} Age: {tom.age}") # Name: Tom Age: 38
Para criar uma data class, importamos do módulo dataclasses
o decorador dataclass
e o aplicamos à classe Person
. Nesse caso, não precisamos mais especificar o construtor, a função __init__
. Simplesmente declaramos os atributos, e o Python irá gerar o construtor, no qual podemos passar os valores para os atributos do objeto.
Assim, reduzimos a definição da classe e a tornamos mais simples. Mas a funcionalidade do decorador dataclass
não se limita à criação do método __init__
. Na realidade, a data class
@dataclass
class Person:
name: str
age: int
é equivalente ao seguinte:
class Person:
def __init__(self, name, age):
self.name = name
self.age = age
def __repr__(self):
return f"Person(name={self.name!r}, age={self.age!r})"
def __eq__(self, other):
if other.__class__ is self.__class__:
return (self.name, self.age) == (other.name, other.age)
return NotImplemented
Nesse caso, vemos que, além da função __init__
, também são definidas as funções __repr__()
para retornar uma representação em string e __eq__()
para comparar dois objetos. Para entender melhor como funciona a sobrecarga de operadores em Python, consulte este artigo. Aplicação dessas funções: Aplicação dessas funções:
from dataclasses import dataclass
@dataclass
class Person:
name: str
age: int
tom = Person("Tom", 38)
bob = Person("Bob", 42)
tomas = Person("Tom", 38)
print(tom == tomas) # True
print(tom == bob) # False
print(tom) # Person(name='Tom', age=38)
Parâmetros do decorador dataclass
Com a ajuda de parâmetros, o decorador dataclass
permite gerar código adicional e personalizar a funcionalidade da data class:
def dataclass(cls=None, /, *, init=True, repr=True, eq=True, order=False,
unsafe_hash=False, frozen=False, match_args=True,
kw_only=False, slots=False)
Vamos considerar os parâmetros básicos:
init
: se forTrue
, gera a função__init__()
. Por padrão, éTrue
.repr
: se forTrue
, gera a função__repr__()
, que retorna uma representação em string do objeto. Por padrão, éTrue
.eq
: se forTrue
, gera a função__eq__()
, que compara dois objetos. Por padrão, éTrue
.order
: se forTrue
, gera as funções__lt__
(operação <),__le__
(<=),__gt__
(>),__ge__
(>=), que são usadas para ordenar objetos. Por padrão, éFalse
.unsafe_hash
: se forTrue
, gera a função__hash__()
, que retorna o hash do objeto. Por padrão, éFalse
.
Além disso, as funções que são criadas por padrão podem ser sobrescritas.
Aplicação dos parâmetros:
from dataclasses import dataclass
@dataclass(unsafe_hash=True, order=True)
class Person:
name: str
age: int
def __repr__(self):
return f"Person. Name: {self.name} Age: {self.age}"
tom = Person("Tom", 38)
print(hash(tom)) # Exemplo de saída: -421667297069596717
print(tom) # Person. Name: Tom Age: 38
Neste caso, ativamos a criação do hash e das funções de ordenação, além de sobrescrever explicitamente a função __repr__
para criar uma representação em string do objeto.
Valores padrão
Se necessário, os atributos podem receber valores padrão caso não sejam passados valores a eles no construtor:
from dataclasses import dataclass
@dataclass
class Person:
name: str
age: int = 18
tom = Person("Tom", 38)
print(tom) # Person(name='Tom', age=38)
bob = Person("Bob")
print(bob) # Person(name='Bob', age=18)
Adicionando funcionalidade extra
Embora as data classes sejam destinadas principalmente ao armazenamento de dados, também é possível definir métodos adicionais:
from dataclasses import dataclass
@dataclass
class Person:
name: str
age: int
def say_hello(self):
print(f"{self.name} says hello")
tom = Person("Tom", 38)
tom.say_hello() # Tom says hello
Documentação oficial: