Why is it permitted to call a class method from an instance? How is it useful to do this?
Why is it permitted to call a class method from an instance? How is it useful to do this?
Suppose we use @classmethod to create an abstract factory:
class Base:
def __init__(self, a, b, c):
self.a, self.b, self.c = a, b, c
@classmethod
def from_string(cls, data):
print(f'Making a {cls.__name__} instance from {data!r}.')
a, b, *c = data.split()
return cls(a, b, c)
class Derived(Base):
pass
Testing it:
>>> d = Derived.from_string('the quick brown fox jumps over the lazy dog')
Making a Derived instance from 'the quick brown fox jumps over the lazy dog'.
>>> d.c
['brown', 'fox', 'jumps', 'over', 'the', 'lazy', 'dog']
By using an instance, we can create an instance that is known to be of the same type as the one we invoked:
>>> d2 = d.from_string('pack my box with five dozen liquor jugs')
Making a Derived instance from 'pack my box with five dozen liquor jugs'.
>>> assert(type(d2) is type(d)) # passes
Whether looked up directly in the class, or from an instance, @classmethod ensures that the class is passed as cls.
It's useful when you have instances of different classes, and the classes have different implementations of the same class method. Calling instance.method() will call the implementation appropriate for that instance's class. You can think of it as a shortcut for
type(instance).method()
Example:
class Class1:
@classmethod
def method(cls):
return "Class 1"
class Class2:
@classmethod
def method(cls):
return "Class 2"
instance = Class1() if random.randint(1, 2) == 1 else Class2()
print(instance.method()) # Prints Class 1 or Class 2
Suppose you've got a class that has six different methods on it. Five of them use self and do something complicated. But the sixth one just returns a constant, because it never changes based on the instance. It would be annoying if I had to remember to call
my_object.get_value()
my_object.frobnicate()
my_object.ping(100)
my_object.foobar(variable)
my_object.add(10)
type(my_object).version_id()
So, for consistency, it makes sense to be able to call that last one (which depends only on the class) on instances as well.