A class method in python differs from an instance method in a couple important ways:
- It binds to a class rather than an instance (hence its name). Thus, its first argument is a class, often called
clsrather than the usual
- It can be called on both an instance of a class and the class itself.
In general, they behave similarly, but one area in which they can differ is when we go to override the class method:
class Spam(object): @classmethod def parrot(cls, message): print cls.__name__, "says:", message class Eggs(Spam): @classmethod def parrot(cls, message): Spam.parrot(cls, message)
This code is broken because
Spam.parrot is already bound to the
Spam class. This means
cls argument will be the
Spam class rather than the
Eggs class that we wanted, so
Spam.parrot will end up being called in the wrong context. Even worse, everything
Eggs.parrot passed to it, including
cls, ends up getting passed as regular arguments, resulting in disaster.
>>>> Spam.parrot("Hello, world!") Spam says: Hello, world! >>>> Eggs.parrot("Hello, world!") Traceback (most recent call last): File "<console>", line 1, in <module> File "<console>", line 4, in parrot TypeError: parrot() takes exactly 2 arguments (3 given)
To chain up to the
Spam class’s implementation we need to use a
super object, which will delegate things the way we want.
class Eggs(Spam): @classmethod def parrot(cls, message): super(Eggs, cls).parrot(message)
There’s a shortcut for the last line if you’re using python 3:
super object functions as a proxy that delegates method calls to a class higher up in the
Eggs class’s hierarchy, and in this case it is critical in ensuring that it gets called in the right context.
>>>> Spam.parrot("Hello, world!") Spam says: Hello, world! >>>> Eggs.parrot("Hello, world!") Eggs says: Hello, world!
If you haven’t used
super() before, here’s what it’s doing:
- The python interpreter looks for
cls.__mro__, a list that represents the order in which the interpreter tries to match method and attribute names to classes.
- The interpreter checks the class dictionary for the next class that follows
Eggsin that list that contains
superobject returns that version of
"parrot", bound to
cls, using the attribute-fetching
Eggs.parrotcalls this bound method,
clsgets passed to
Spam.parrotin place of the
In general I tend to stick with the older-style syntax for chaining method calls, but this is one case where
super() is simply indispensable.