Dynamic attributes with __getattr__()

We can create objects where attributes are computed from a single, centralized __getattr__() method. When attribute values are computed by separate properties, the presence of many methods can be a convenient way to encapsulate a variety of algorithms. In some cases, however, it might be sensible to combine all of the computations into a single method. In this case, the names of the attributes are essentially invisible to mypy, since they aren't an obvious part of the Python source text.

A single computation method is shown in the following example:

class RTD_Solver:

def __init__(
self, *,
rate: float = None,
time: float = None,
distance: float = None
) -> None:
if rate:
self.rate = rate
if time:
self.time = time
if distance:
self.distance = distance

def __getattr__(self, name: str) -> float:
if name == "rate":
return self.distance / self.time
elif name == "time":
return self.distance / self.rate
elif name == "distance":
return self.rate * self.time
else:
raise AttributeError(f"Can't compute {name}")

An instance of the RTD_Solver class is built with two of three values. The idea is to compute the missing third value from the other two. In this case, we've elected to make the missing value an optional attribute, and compute the value of the attribute when required. The attributes for this class are dynamic: two of the three possible attributes will be in use.

The class is used as shown in the following snippet:

>>> r1 = RTD_Solver(rate=6.25, distance=10.25)
>>> r1.time
1.64
>>> r1.rate
6.25

An instance of the RTD_Solver class was built with two of the three possible attributes. In this example, it's rate and distance. A request for the time attribute value leads to a computation of time from rate and distance.

A request for the rate attribute value, however, does not involve the __getattr__() method. Because the instance has rate and distance attributes, these are provided directly. To confirm that __getattr__() is not used, insert a print() function in the computation of rate, as shown in the following code snippet:

if name == "rate":
print("Computing Rate")
return self.distance / self.time

When an instance of RTD_Solver is created with an attribute value set by the __init__() method, the __getattr__() method is not used to fetch the attribute. The __getattr__() method is only used for unknown attributes.

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

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