I am trying to figure out difference between @staticmethod
and @classmethod
. The latter is passed a cls
instance.
When I tried to call the @classmethod
, it is giving me an error.
How should I call a @classmethod
(to_c()
and to_f()
) decorated method in REPL?
Here is the REPL calls
>>> from temperature_converter import *
>>> c = TemperatureConverter(41)
>>> TemperatureConverter.to_f()
Traceback (most recent call last):
File "<stdin>", line 1, in <module>
File "C:\Repos\Python\module-3\temperature_converter.py", line 21, in to_f
return cls.c_to_f(cls.temperature)
File "C:\Repos\Python\module-3\temperature_converter.py", line 25, in c_to_f
return celsius * 9 / 5 + 32
TypeError: unsupported operand type(s) for *: 'property' and 'int'
Here is the class, TemperatureConverter
class TemperatureConverter:
_temperature = 0
@property
def temperature(self):
return self._temperature
@temperature.setter
def temperature(self, value):
self._temperature = value
def __init__(self, temperature):
self.temperature = temperature
@classmethod
def to_c(cls):
return cls.f_to_c(cls.temperature)
@classmethod
def to_f(cls):
return cls.c_to_f(cls.temperature)
@staticmethod
def c_to_f(celsius):
return celsius * 9 / 5 + 32
@staticmethod
def f_to_c(fahrenheit):
return (fahrenheit - 32) * 5/9
Problem: You trying to access the temperature
property from the class and expecting the same result as an instance, in this case an int
value of 41
. However, attributes are called differently in instances and classes. Compare the __get__
method for each:
Description
# Reassignments for illustration
C = TemperatureConverter # class
i = c # instance
attr = "temperature" # attribute
# Call via Instance binding, `c.temperature`
C.__dict__[attr].__get__(i, C)
# 41
# Call via Class binding, `C.temperature`
C.__dict__[attr].__get__(None, C)
# <property at 0x4ab9458>
Here is more information on the signature of the __get__
method and a SO post on how descriptors work. In this case, the signature is could be seen as C.__get__(self, inst, cls)
. In short, when getting from a property, unlike an instance calls, calls from a class passes None
for the instance argument.
As shown above, the property object is returned if bound to the class:
C.temperature
# <property at 0x4ab9458>
Can we still "get" the value from the property object? Let us call __get__
on the property object:
C.temperature.__get__(i, C)
# 41
The latter shows it is possible to get the property value while bound to a class. While it may be tempting to implement the __get__
method on cls.temperature
inside your class method, you still need to pass in an instance (e.g. i
) to access the value of the property. Instances are not accessible in a class method. Therefore, we see why the property object is returned in your code, which is trying to multiply with an int
and raises the error you observe.
This is one explanation for your problem, in particular describing why you cannot access your property value cls.temperature
within a class method.