I have an application where I need to create many different classes. For each class I must write the __init__
, the setters and getters , the supporting functions like __repr__
and , __str__
and other bureaucratic minutiae.
Is there a shorter way to build a class?
There is a short way to build a class with a default implementation of the methods you mention. To illustrate its use, we will take as an example a Dietetic Clinic that needs to keep a record of patients with name, sex and current weight.
dataclass library
The dataclasses library is standard since Python 3.7, although it can be used in Python 3.6 by installing the
dataclasses
. This gives us the decoratordataclass
that we prepend to the class declaration. With that, the declaration itself is reduced to listing its attributes, each with its type according to PEP 526 -- Syntax for Variable Annotations(you can use
Any
as type when the attribute is of indeterminate type).and with this we already have implementing the method
__init__
, the setters and getters for each attribute and also the method behind the functionrepr
.The method
__init__
declares its named parameters in the same order as they appear in the class declaration. Objects can be instantiated by passing parameters by position or by name. In this case, the generated statement is:default values
It is possible to indicate default values for each attribute. This is done by typing the "=" sign followed by a valid expression for the attribute type.
Let's make the default gender for patients "F":
Attributes without default values must be written first, followed by all those with default.
Sort and Compare
For the purposes of scheduling fitness classes, you need to sort and classify patients by weight. For that we normally add to the class the comparison methods
__eq__
,__lt__
, etc., but it is something thatdataclasses
can do it for us.To generate the comparison methods,
dataclasses
consider the object as a tuple of attributes (in the order of their declaration) that is compared against the tuple representing the other object (of the same class). The comparison follows the rules of tuple comparison.So if we want to compare patients by weight, the attribute
peso
must be the first in the declaration. We also need to tell the decorator to generate the necessary comparison methods. For this, we pass the parameterorder=True
to the decoratorThe decorator accepts several named parameters (shown with their default):
where:
init
generate method__init__
repr
generate method__repr__
eq
generate method__eq__
order
Generate methods__lt__(), __le__(), __gt__(), __ge__()__
frozen
Makes the class immutable.own methods
Nothing prevents you from adding your own methods to the class. For example, the standard implementation of
str
is limited to callingrepr
, which is not ideal. Let's do our own implementation:Attribute Settings
The method
field
allows us to set a particular attribute of the class. With this, the complete declaration of an attribute is:(All parameters are optional)
The parameter
init
determines whether the field will be initialized via__init__
or is an internally generated field. Likewise,repr
it marks whether the field should be included when calling__repr__
andcompare
whether it should be included when generating comparisons__lt__
,__gt__
, etc.The parameter appears assigned with a magic
default
variable MISSING. This is used so that "has no default" can be distinguished from "default is None".Alternatively, the default value can be determined by a parameterless function specified in "default_factory". This is called each time an object is created, and must supply the appropriate default value.
Let's expand our class to include a list of weekly weights. We want the attribute not to be included as a creation parameter, to force it to always be initialized with an empty list.
The declaration
is syntactically invalid, and prevents us from the common mistake of unintentionally sharing a list among all objects of the Patient class. The correct way is to use the parameter
default_factory
to indicate how to generate a new list for each patient:and then
utility methods
The methods
asdict
andastuple
allow to obtain the object as a dictionary or a tuple, respectively:The generated method
__init__
by default executes the method__post_init__()
if it is defined. The idea is to be able to complete the initialization after it__init__
receives its parameters.Let's add code to our class that initializes the list of weekly weights with the person's starting weight.
I hope they have been useful to you.