Welcome, guest | Sign In | My Account | Store | Cart

This recipe demonstrates 'real' class methods, like they are known from Smalltalk.

Class methods implicitely receive the actual class as the first parameter.

They are inherited by subclasses, and may as well be overridden.

Class methods may return anything, although they are particularly useful as constructors.

Python, 67 lines
 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
class _ClassMethod:
    # Helper class: instances represent methods bound to a class
    def __init__(self, klass, func):
        self.func = func
        # oops: circular reference!
        self.klass = klass

    def __call__(self, *args, **kw):
        # return result of function call
        return self.func(self.klass, *args, **kw)

import ExtensionClass

class Class(ExtensionClass.Base):
    def __class_init__(self):
        # automagically convert methods to class methods
        #
        # ExtensionClass.Base calls this _class_ method after the _class_
        # has been created!
        for mth in self.__class_methods__:
            self.__dict__[mth.func_name] = _ClassMethod(self, mth)
        
    def class_method(self, *args, **kw):
        # A _class method_ creating an returning new a new instance
        # the 'self' argument is actual class itself
        print "class_method called with", self, args, kw
        return self()

    def class_method_2(self, *args, **kw):
        # Class methods may return anything they like
        print "class_method_2 called with", self, args, kw
        return None

    def instance_method(self, *args, **kw):
        # instance methods behave normally
        print "instance_method called with", self, args, kw

    # List the methods which should be automagically be converted
    # into class methods
    __class_methods__ = class_method, class_method_2

class Subclass(Class):
    def class_method_2(self):
        # override a class method in a subclass
        print "Hi from", self


####################################################################

c = Class()

# demonstrate calling class methods
print Class.class_method()
print Class.class_method(1, 2, 3, a=10, b=20, c=30)
print Subclass.class_method()
print Class.class_method_2()
print Subclass.class_method_2()

# demonstrate calling instance methods
c.instance_method()
try:
    Class.instance_method()
except Exception, detail:
    print "Called instance method off the class:\n\t", detail

# calling class methods off an instance also works...
print c.class_method()

The magic done by the __class_init__ class method is triggered by deriving from Jim Fulton's ExtensionClass, which is contained in the ZOPE distribution.

It may be possible to achive the same effect with pure Python code by using Metaclasses, but this is beyond the scope of this recipe.

2 comments

Brent Burley 23 years ago  # | flag

Confusing; Zope dependency unclear and not explained. This recipe is only a slight variation on Alex Martelli's recipe, "Static-methods" (aka "class-methods") in Python, which is also more concise and better explained. The main difference is that Thomas' variation passes the class object as the first parameter to the class method which allows inheritence.

The Zope dependency apparently comes from the need to have access to the class object while the class is being defined. The Zope ExtensionClass.Base class extends the Python class model by calling a __class_init__(classObject) method immediately after the class has been defined. The Zope ExtensionClass is a C extension which makes it inaccessible to non-Zope users.

Either the recipe should be altered to not depend on Zope, or the Zope dependency should be more clearly explained. Brent Burley

Bruno Desthuilliers 18 years, 1 month ago  # | flag

Out of date. Python now has classmethods (à la Smalltalk) and staticmethods (à la Java).