- Mastering Objectoriented Python
- Steven F. Lott
- 418字
- 2021-11-12 16:25:14
Using a nondata descriptor
We often have small objects with a few tightly bound attribute values. For this example, we'll take a look at numeric values that are bound up with units of measure.
The following is a simple nondata descriptor class that lacks a __get__()
method:
class UnitValue_1: """Measure and Unit combined.""" def __init__( self, unit ): self.value= None self.unit= unit self.default_format= "5.2f" def __set__( self, instance, value ): self.value= value def __str__( self ): return "{value:{spec}} {unit}".format( spec=self.default_format, **self.__dict__) def __format__( self, spec="5.2f" ): #print( "formatting", spec ) if spec == "": spec= self.default_format return "{value:{spec}} {unit}".format( spec=spec, **self.__dict__)
This class defines a simple pair of values, one that is mutable (the value) and another that is effectively immutable (the unit).
When this descriptor is accessed, the descriptor object itself is made available, and other methods or attributes of the descriptor can then be used. We can use this descriptor to create classes that manage measurements and other numbers associated with physical units.
The following is a class that does rate-time-distance calculations eagerly:
class RTD_1: rate= UnitValue_1( "kt" ) time= UnitValue_1( "hr" ) distance= UnitValue_1( "nm" ) def __init__( self, rate=None, time=None, distance=None ): if rate is None: self.time = time self.distance = distance self.rate = distance / time if time is None: self.rate = rate self.distance = distance self.time = distance / rate if distance is None: self.rate = rate self.time = time self.distance = rate * time def __str__( self ): return "rate: {0.rate} time: {0.time} distance: {0.distance}".format(self)
As soon as the object is created and the attributes loaded, the missing value is computed. Once computed, the descriptor can be examined to get the value or the unit's name. Additionally, the descriptor has a handy response to str()
and formatting requests.
The following is an interaction between a descriptor and the RTD_1
class:
>>> m1 = RTD_1( rate=5.8, distance=12 ) >>> str(m1) 'rate: 5.80 kt time: 2.07 hr distance: 12.00 nm' >>> print( "Time:", m1.time.value, m1.time.unit ) Time: 2.0689655172413794 hr
We created an instance of RTD_1
with rate
and distance
arguments. These were used to evaluate the __set__()
methods of the rate
and distance
descriptors.
When we asked for str(m1)
, this evaluated the overall __str__()
method of RTD_1
that, in turn, used the __format__()
method of the rate, time, and distance descriptors. This provided us with numbers with units attached to them.
We can also access the individual elements of a descriptor since nondata descriptors don't have __get__()
and don't return their internal values.