Credit: Thomas Heller
You want to call methods directly on a class without having to supply an instance, and with the class itself as the implied first argument.
In Python 2.2 (on either classic or new-style classes), the new
built-in
classmethod
function wraps any callable into a class method, and we just bind the
same name to the classmethod
object in class
scope:
class Greeter: def greet(cls, name): print "Hello from %s"%cls._ _name_ _, name greet = classmethod(greet)
In Python 2.1 or earlier, we need a wrapper that is slightly richer than the one used for static methods in Recipe 5.7:
class classmethod: def _ _init_ _(self, func, klass=None): self.func = func self.klass = klass def _ _call_ _(self, *args, **kw): return self.func(self.klass, *args, **kw)
Furthermore, with this solution, the following rebinding is not sufficient:
greet = classmethod(greet)
This leaves greet.klass
set to
None
, and if the class inherited any class methods
from its bases, their klass
attributes would also
be set incorrectly. It’s possible to fix this by
defining a function to finish preparing a class object and always
explicitly calling it right after every class
statement. For example:
def arrangeclassmethods(cls): for attribute_name in dir(cls): attribute_value = getattr(cls, attribute_name) if not isinstance(attribute_value, classmethod): continue setattr(cls, classmethod(attribute_value.func, cls))
However, this isn’t completely sufficient in Python
versions before 2.2, since, in those versions, dir
ignored inherited attributes. We need a recursive walk up the bases
for the class, as in Recipe 5.3. But a worse
problem is that we might forget to call the
arrangeclassmethods
function on a class object
right after its class
statement.
For older Python versions, a better solution is possible if you have
Jim Fulton’s
ExtensionClass
class. This class is the heart of Zope,
so you have it if Zope is installed with Python 2.1 or earlier. If
you inherit from
ExtensionClass.Base
and define a method called _ _class_init_ _
, the method is
called with the class object as its argument after the class object
is built. Therefore:
import ExtensionClass class ClassWithClassMethods(ExtensionClass.Base): def _ _class_init_ _(cls): arrangeclassmethods(cls)
Inherit from
ClassWithClassMethods
directly or indirectly, and
arrangeclassmethods
is called automatically on your class when it’s
built. You still have to write a recursive version of
arrangeclassmethods
for generality, but at least
the problem of forgetting to call it is solved.
Now, with any of these solutions, we can say:
>>> greeting = Greeter( ) >>> greeting.greet("Peter") Hello from Greeter Peter >>> Greeter.greet("Paul") Hello from Greeter Paul
Real class methods, like those in Smalltalk, implicitly receive the
actual class as the first parameter and are inherited by subclasses,
which can override them. While they can return anything, they are
particularly useful as
factory methods (i.e., methods that
create and return instances of their classes). Python 2.2 supports
class methods directly. In earlier releases, you need a wrapper, such
as the classmethod
class shown in this recipe,
and, more problematically, you need to arrange the wrapper objects
right after you create a class, so that the objects refer to the
actual class when you call them later.
Zope’s ExtensionClass
helps with
the latter part. Metaclasses should also help you achieve the same
effect, but, since they were hard to use before Python 2.2, and the
likeliest reason to still use Python 2.1 is that you use a version of
Zope that requires Python 2.1, this should be avoided. The point is
that statements in the class body execute before the class object is
created, while our arranging needs to take place after that. Classes
that inherit from ExtensionClass.Base
solve this
problem for us, since their _ _class_init_ _
method automatically executes just after the class object is created,
with the class object itself as the only argument. This is an ideal
situation for us to delegate to our
arrangeclassmethods
function.
In Python 2.2, the wrapping inside the class body suffices because
the new built-in type classmethod
does not need to
access the class object at the point of creation, so
it’s not an issue if the class object does not yet
exist when the class methods are wrapped. However, notice that you
have to perform the wrapping again if a subclass overrides particular
class methods (not, however, if they inherit them).
Recipe 5.7;
ExtensionClass
is not available as a standalone
class, but is part of Zope (http://www.zope.org).
Get Python Cookbook now with the O’Reilly learning platform.
O’Reilly members experience books, live events, courses curated by job role, and more from O’Reilly and nearly 200 top publishers.