Using the dataclasses module

Starting with Python 3.7 the dataclasses module is available. This module offers a superclass we can use to create classes with clearly-stated attribute definitions. The core use case for a dataclass is a simple definition of the attributes of a class.

The attributes are used to automatically create common attribute access methods, including __init__(), __repr__(), and __eq__(). Here's an example:

from dataclasses import dataclass
from typing import Optional, cast

@dataclass
class RTD:
rate: Optional[float]
time: Optional[float]
distance: Optional[float]

def compute(self) -> "RTD":
if (
self.distance is None and self.rate is not None
and self.time is not None
):
self.distance = self.rate * self.time
elif (
self.rate is None and self.distance is not None
and self.time is not None
):
self.rate = self.distance / self.time
elif (
self.time is None and self.distance is not None
and self.rate is not None
):
self.time = self.distance / self.rate
return self

Each instance of this class will have three attributes, rate, time, and distance. The decorator will create an __init__() method to set these attributes. It will also create a __repr__() method to display the details of the attribute value. An __eq__() method is written to perform a simple equality check on all of the attribute values.

Careful checking for None and non-None values is helpful for mypy. This explicit checking provides an assurance that the Optional[float] types will have non-None values.

Note that the three names are written as part of the class definition. They're used to build an __init__() method that's part of the resulting class. These will become instance variables in the resulting objects.

The compute() method changes the internal state of the object. We've provided a type hint that describes the return value as an instance of the class. Here's how we can use an instance of this class:

>>> r = RTD(distance=13.5, rate=6.1, time=None)
>>> r.compute()
RTD(rate=6.1, time=2.2131147540983607, distance=13.5)

In this code snippet, we created an instance, providing non-None values for distance and rate. The compute() method computed a value for the time attribute.

The default @dataclass decorator will not have comparison methods. It will create a mutable class where attribute values can be changed. 

We can request some additional, optional features. We can provide optional parameters to the decorator to control optional features. We can create a class for immutable objects with comparison operators with code such as the following:

@dataclass(frozen=True, order=True)
class Card:
rank: int
suit: str

@property
def points(self) -> int:
return self.rank

The frozen parameter in this example leads the decorator to make the class into an immutable, frozen object. The order parameter to the @dataclass decorator creates the methods for comparison in the class definition. This is very helpful for creating simple, immutable objects. Because the two attributes include type hints, mypy can confirm that the Card dataclass is used properly. 

Inheritance works with dataclasses. We can declare classes as in the following example:

class Ace(Card):

@property
def points(self) -> int:
return 1


class Face(Card):

@property
def points(self) -> int:
return 10

These two classes inherit the __init__(), __repr__(), __eq__(), __hash__(), and comparison methods from the Card superclass. These two classes differ in the implementation of the points() method.

The @dataclass decorator simplifies the class definition. The methods that tend to have a direct relationship with the attributes are generated by the decorator.

..................Content has been hidden....................

You can't read the all page of ebook, please click here login for view all page.
Reset