Modulul dataclass. Clasele de date
Modulul dataclasses oferă decoratorul @dataclass, care permite crearea claselor de date (data classes), reducând astfel semnificativ codul boilerplate al claselor. De regulă, aceste clase sunt destinate stocării unor stări sau date și nu necesită comportamente complexe sub forma funcțiilor.
Exemplu simplu:
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
Aici am definit clasa Person, care are două atribute în constructor: name și age. Apoi am creat un obiect al acestei clase și am afișat valorile atributelor sale.
Acum să modificăm această programă, transformând clasa Person într-o clasă de date:
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
Pentru a crea o clasă de date, importăm decoratorul @dataclass din modulul dataclasses și îl aplicăm clasei Person. În acest caz, nu mai trebuie să specificăm explicit constructorul __init__. Doar indicăm atributele, iar Python va genera automat constructorul, permițându-ne să transmitem valori pentru atributele obiectului.
Astfel, am simplificat definirea clasei. Dar funcționalitatea decoratorului @dataclass nu se limitează la generarea metodei __init__. În realitate, clasa de date:
@dataclass
class Person:
name: str
age: int
este echivalentă cu următoarea:
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
Pe lângă funcția __init__, sunt generate automat și funcțiile __repr__() pentru a oferi o reprezentare textuală și __eq__() pentru a compara două obiecte. Exemplu de utilizare a acestor funcții:
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)
Parametrii decoratorului @dataclass
Cu ajutorul parametrilor, decoratorul @dataclass poate genera cod suplimentar și permite personalizarea generării codului:
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)
Parametrii principali:
- init: dacă este True, se generează funcția __init__(). Implicit este True
- repr: dacă este True, se generează funcția __repr__(), care returnează o reprezentare textuală a obiectului. Implicit este True
- eq: dacă este True, se generează funcția __eq__(), care compară două obiecte. Implicit este True
- order: dacă este True, se generează funcțiile __lt__ (operația <), __le__ (<=), __gt__ (>), __ge__ (>=), care sunt utilizate pentru ordonarea obiectelor. Implicit este False
- unsafe_hash: dacă este True, se generează funcția __hash__(), care returnează hash-ul obiectului. Implicit este False
Funcțiile generate implicit pot fi suprascrise.
Exemplu de utilizare a parametrilor:
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(tom.__hash__()) # -421667297069596717
print(tom) # Person. Name: Tom Age: 38
În acest caz, activăm generarea hash-ului și a funcțiilor de ordonare, precum și suprascriem explicit funcția __repr__ pentru a crea o reprezentare textuală personalizată a obiectului.
Valori implicite
Dacă este necesar, atributelor li se pot atribui valori implicite în cazul în care constructorul nu primește valori pentru acestea:
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)
Adăugarea de funcționalitate suplimentară
Deși clasele de date sunt destinate în principal stocării de date, ele pot include și metode suplimentare pentru a adăuga funcționalitate:
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