3.15 __getattr__
Note
Click here to download the full example code or to run this example in your browser via Binder
3.15 __getattr__#
This lesson describes the usage of __getattr__
If a class
(more correctly ab object) does not have an attribute and we try to access this attribute
we will get AttributeError
.
class Human:
pass
h = Human()
# uncomment following 1 line
# h.horns
The Human class does not have an attribute horns and therefore when we run h.horns, we will get AttributeError
However, if we want to avoid such an error, we can overwrite
__getattr__
method of the class. This method must take one input
argument.
class Human:
def __getattr__(self, item):
print(f"attribute {item} has not been set to Human")
return
h = Human()
print(h.horns)
attribute horns has not been set to Human
None
When python tries to search attributes of a class, then __getattr__
method is called at the end of its search. If this method is not
overwritten by the user, python will raise AttributeError
, as it
was done earlier.
One advantage/usage of this method is what we can call dynamic attribute creation.
TempUnitConverter = {
"FAHRENHEIT": {
"Fahrenheit": lambda fahrenheit: fahrenheit, # fahrenheit to Centigrade
"Kelvin": lambda fahrenheit: [(x + 459.67) * 5/9 for x in fahrenheit], # fahrenheit to kelvin
"Centigrade": lambda fahrenheit: [(x - 32.0) / 1.8 for x in fahrenheit] # fahrenheit to Centigrade
},
"KELVIN": {
"Fahrenheit": lambda kelvin: [x * 9/5 - 459.67 for x in kelvin], # kelvin to fahrenheit
"Kelvin": lambda k: k, # Kelvin to Kelvin
"Centigrade": lambda kelvin: [x - 273.15 for x in kelvin] # kelvin to Centigrade}
},
"CENTIGRADE": {
"Fahrenheit": lambda centigrade: [x * 1.8 + 32.0 for x in centigrade], # Centigrade to fahrenheit
"Kelvin": lambda centigrade: [x + 273.15 for x in centigrade], # Centigrade to kelvin
"Centigrade": lambda centigrade: centigrade
}
}
class Temperature(object):
"""
The idea is to write the conversion functions in a dictionary and
then dynamically create attribute if the attribute
is present in converter as key otherwise raise WongUnitError.
converts temperature among units [kelvin, centigrade, fahrenheit]
"""
def __init__(self, val, input_unit):
self.val = val
self.input_unit = input_unit
def __getattr__(self, out_unit):
# pycharm calls this method for its own working, executing default behaviour at such calls
if out_unit.startswith('_'):
return self.__getattribute__(out_unit)
else:
if out_unit not in TempUnitConverter[self.input_unit]:
raise ValueError(f"output in {out_unit} is not allowed. Allowed units are: ", self.allowed)
val = TempUnitConverter[self.input_unit][str(out_unit)](self.val)
return val
@property
def allowed(self):
return list(list(TempUnitConverter.values())[0].keys())
@property
def input_unit(self):
return self._input_unit
@input_unit.setter
def input_unit(self, in_unit):
if in_unit.upper() == 'CELSIUS':
in_unit = 'CENTIGRADE'
if in_unit.upper() not in TempUnitConverter:
raise ValueError(f"Input in {in_unit} is not allowed", self.allowed)
self._input_unit = in_unit.upper()
temp = [i for i in range(10)]
T = Temperature(temp, 'Centigrade')
print(T.Kelvin)
[273.15, 274.15, 275.15, 276.15, 277.15, 278.15, 279.15, 280.15, 281.15, 282.15]
print(T.Fahrenheit)
[32.0, 33.8, 35.6, 37.4, 39.2, 41.0, 42.8, 44.6, 46.4, 48.2]
print(T.Centigrade)
[0, 1, 2, 3, 4, 5, 6, 7, 8, 9]
Above we did not explicitly defined Kelvin
, Fahrenheit
or Centigrade
attributes of the Temperature
class.
but these attributes are created after __getattr__
method is called.
T = Temperature(temp, 'Fahrenheit')
print(T.Centigrade)
print(T.Kelvin)
[-17.77777777777778, -17.22222222222222, -16.666666666666668, -16.11111111111111, -15.555555555555555, -15.0, -14.444444444444445, -13.88888888888889, -13.333333333333332, -12.777777777777777]
[255.3722222222222, 255.92777777777778, 256.48333333333335, 257.0388888888889, 257.59444444444443, 258.15, 258.7055555555555, 259.2611111111111, 259.81666666666666, 260.3722222222222]
T = Temperature(temp, 'Kelvin')
print(T.Centigrade)
print(T.Fahrenheit)
[-273.15, -272.15, -271.15, -270.15, -269.15, -268.15, -267.15, -266.15, -265.15, -264.15]
[-459.67, -457.87, -456.07, -454.27000000000004, -452.47, -450.67, -448.87, -447.07, -445.27000000000004, -443.47]
- Questions:
Why Temperature(temp, ‘Celsius’).Kelvin works but not Temperature(temp, ‘Celsius’).Celsius?
Change the Temperature class so that T.centigrade gives same answer as that of T.Centigrade.
Change the Temperature class so that T.Celsius also works.
Total running time of the script: ( 0 minutes 0.004 seconds)