Python Dataclass Decorator.
The dataclass
decorator is used to automatically generate special methods to classes,
including __str__
and __repr__
. It helps reduce some boilerplate
code. The dataclass
decorator is located in the dataclasses
module.
The dataclass
decorator examines the class to find fields. A field
is defined as class variable that has a type annotation.
@dataclass class Test: ... @dataclass() class Test: ... @dataclass(init=True, repr=True, eq=True, order=False, unsafe_hash=False, frozen=False) class Test: ...
These three declarations are equivalent. If no parameters are set in the
decorator, the default ones are used. If the init
parameter is set
to True
, the __init__
method will be generated. If
the class already defines the __init__
, the parameter is ignored.
If the repr
parameter is set to True
, the
__repr__
method will be generated. If the class already defines
the __repr__
, the parameter is ignored. If the eq
parameter is set to True
, the __eq__
method will be
generated. If the class already defines __eq__
, this parameter is
ignored.
If the order
parameter is set to True
, the
__lt__
, __le__
, __gt__
, and
__ge__
methods are generated. If the class already defines
any of the methods, the ValueError
is raised.
If the unsafe_hash
is defined to False
,
the __hash__
method is generated according
to how eq
and frozen
are set. If the frozen
parameter is set to True
, the assignment to fields will generate an
exception.
Python regular custom class
In a regular custom Python class, we provide a constructor
and other methods such as __repr__
manually.
#!/usr/bin/env python class Person: def __init__(self, name, age): self.name = name self.age = age def __repr__(self): return f'Person{{name: {self.name}, age: {self.age}}}' p = Person('John Doe', 34) print(p)
The example shows a Person
class with a constructor and
the __repr__
method, which gives a complete representation
of the object.
$ ./regular_class.py Person{name: John Doe, age: 34}
Python dataclass example
The following example shows a simple usage of the
dataclass
decorator.
#!/usr/bin/env python from dataclasses import dataclass @dataclass class Person: name: str age: int p = Person('John Doe', 34) print(p)
We have a class with two fields: name
and str
.
from dataclasses import dataclass
The dataclass
decorator is located in the dataclasses
module.
@dataclass class Person: name: str age: int
We apply the dataclass
decorator on the Person
class.
p = Person('John Doe', 34) print(p)
A new person object is created. Its __init__
method
is called, which is auto-generated by the dataclass
decorator.
$ ./simple_dataclass.py Person(name='John Doe', age=34)
Python dataclass default values
It is possible to provide default values to the fields.
#!/usr/bin/env python from dataclasses import dataclass @dataclass class Person: name: str = 'unknown' age: int = 0 p = Person('John Doe', 34) print(p) p2 = Person() print(p2)
In the example, the Person
class has two fields; the
fields have some default values.
@dataclass class Person: name: str = 'unknown' age: int = 0
With the assignment operator (=), we give the fields default values.
p2 = Person() print(p2)
When we do not provide values in the constructor, the fields will have default values.
$ ./default_values.py Person(name='John Doe', age=34) Person(name='unknown', age=0)
The dataclass frozen parameter
If the frozen
parameter is set to True
,
we cannot assign values to fields.
#!/usr/bin/env python from dataclasses import dataclass @dataclass(frozen=True) class Person: name: str age: int p = Person('John Doe', 34) p.occupation = 'gardener' print(p) print(p.occupation)
In the example, the frozen
parameter is set
to True
. The program fails with the following error
message: dataclasses.FrozenInstanceError: cannot assign to field 'occupation'
.
The dataclass asdict function
The asdict
function converts a dataclass instance to a
dict of its fields.
#!/usr/bin/env python from dataclasses import dataclass, asdict @dataclass class Person: name: str occupation: str age: int p = Person('John Doe', 'gardener', 34) print(p) print(asdict(p))
In the example, we print the fields of the Person
class with
the help of the asdict
function.
$ ./as_dict_fun.py Person(name='John Doe', occupation='gardener', age=34) {'name': 'John Doe', 'occupation': 'gardener', 'age': 34}
The first line is the output of the __repr__
method.
The second line is the dictionary of the fields.
The dataclass field function
With the field
function, we can provide
some additional per-field information.
#!/usr/bin/env python from dataclasses import dataclass, field @dataclass class Person: name: str age: int occupation: str = field(init=False, repr=False) p = Person('John Doe', 34) print(p) p.occupation = "Gardener" print(f'{p.name} is a {p.occupation}')
In the example, we have an additional occupation
field.
occupation: str = field(init=False, repr=False)
The occupation
field is not included in the __init__
and __repr__
methods.
$ ./fields.py Person(name='John Doe', age=34) John Doe is a Gardener* Below is an example usage:
#!/usr/bin/env python from dataclasses import dataclass, asdict, astuple, field from typing import ClassVar @dataclass (order=True, frozen=False) class Person: name: str age: int eye_color: str = field(default="black") headers: dict = field (init=False, default_factory=dict) # if we need to add an immutable object as a default value we can use default_factory. height: int = field (init=False) sort_index: int = field (init=False, repr=False) class_var: ClassVar = field(init=False, default=1) def __post_init__(self): # fixing dependent vars values self.height = 100 if self.eye_color == "blue" else "black" # set sort_index self.sort_index = self.age if __name__ == "__main__": p1 = Person ("ami", 11) Person.class_var += 1 p2 = Person ("ami", 114) print(p1.__repr__()) # Get all fields if p1 < p2: p1.age = 99 print (asdict (p1)) print (astuple (p1)) else: print (asdict (p2)) print (astuple (p2)) print(Person.class_var)