Does Python have Class methods

James_Althoff at i2.com James_Althoff at i2.com
Tue May 8 16:39:48 EDT 2001


Aahz writes
>In article <3af80f95.73151117 at News.CIS.DFN.DE>,  <costas at meezon.com>
wrote:
>>
>>Does Python support the concept of class methods (as opposed to
>>instance methods)? If not is there a suggested way of doing this?
>
>The standard answer is to put the class (e.g. "foo") into its own module
>(e.g. "foo.py") and use module-level functions.
>--
>                      --- Aahz  <*>  (Copyright 2001 by aahz at pobox.com)

Another standard answer is to wrap each class function in a "Callable"
class and reassign the class attribute to the Callable instance in order to
bypass Python's "unbound method" mechanism.  Another answer is to use an
existing patch or hope for a new Python release in which the
"first-arg-class-type-enforcement" check is removed.

Each of these workarounds does the job for many applications of "class
methods".  However, they all have the shortcoming that they really
implement class *functions* -- like in Java and C++ -- and not class
*methods* -- like in Smalltalk and Ruby.  The key difference is that
*methods* support a *virtual* mechansim.  Functions don't.  An example of
where a virtual mechanism for class *methods* might be handy is in factory
methods.

For example, suppose I have Class1.  I want a class method that makes a new
instance of Class1 for me.  I also want a class method that makes a list of
new instances of Class1.

I could use the "module level function" approach and do (or many
variations):

def new():
    return Class1()

def newList(n=10):
    l = []
    for i in range(n):
        newInstance = new()
        l.append(newInstance)
    return l

Now suppose I have Class2 that derives from Class1.  It would be very nice
to be able to reuse the "newList" factory function as is.  But (assuming
that the "module function" recipe is being used) there is no inheritance
for module functions.  So I have to define "newList" in a Class2 module and
have it delegate to the Class1 module "newList".  But even then "newList"
will return instances of Class1 not instances of Class2.  What is needed is
the ability to override the "new" function so that it does "return Class2
()".  But there is no mechanism to do this because "new" and "newList" are
functions and not methods.  The same applies when using the "Callable
wrapper" recipe.  To work around this limitation, you have to define these
functions to take a class object as a function argument.

Something like:

def new(classObject):
    return classObject()

def newList(classObject,n=10):
    l = []
    for i in range(n):
        newInstance = new(classObject)
        l.append(newInstance)
    return l

So you have the awkwardness of having to include the class object in the
function, passing it into any other functions that require it, and also
passing it in from the caller.  In the case of the "Callable" wrapper
recipe -- which at least supports inheritance -- your invoking code would
look like:

myList = Class2.newList(Class2,5)

which is awkward.

What you really want (well, what I really want, anyway :-) ) are class
*methods* not class *functions*.  To have class *methods* you need a bit
more "object-oriented-ness".  In Python a class is an object, but it is not
an instance.  Contrast this with Smalltalk and Ruby.  In those languages a
class is an object AND it is an instance as well.  What is a class object
an instance of?  It is an instance of a "metaclass".  A metaclass is just a
class whose instances are class objects (typically these are singleton
instances).  By making a class object an instance of a metaclass you can
now define true class *methods* -- because a class method is the same as
any other method.  The "bound instance" (i.e., the "receiver") is a class
object.

In such a scenario using the example above, "new" and "newList" would be
defined as methods in a Class1MetaClass.  A Class2MetaClass could derive
from Class1MetaClass.  Class2MetaClass could then override the "new" method
and reuse the "newList" method that it inherited from Class1MetaClass as is
-- which was the original goal.

Of course one can devise ways of coding "metaclasses" in Python as in the
example below.  But any such "recipe" requires extra wrapping and
conventions that would likely discourage its use.


class MetaClass:

    def __init__(self, classObject):
        self.classObject = classObject

    def __call__(self, *pargs, **kargs):
        return self.classObject(*pargs, **kargs)

    def whoami(self):  # for testing
        print 'MetaClass.whoami:', self


class Class1Meta(MetaClass):

    def new(self):
        return self.classObject()

    def newList(self,n=3):
        l = []
        for i in range(n):
            newInstance = self.new()
            l.append(newInstance)
        return l


class Class2Meta(Class1Meta):

    def new(self):
        return self.classObject()


class Class1:

    def whoami(self):  # for testing
        print 'Class1.whoami:', self

Class1 = Class1Meta(Class1)


class Class2(Class1.classObject):

    def whoami(self):  # for testing
        Class1.classObject.whoami(self)
        print 'Class2.whoami:', self

Class2 = Class2Meta(Class2)


# Test

print
Class1.whoami()
Class2.whoami()

print
Class1().whoami()
Class2().whoami()

print
Class1.new().whoami()
Class2.new().whoami()

print
print Class1.newList()
print Class2.newList()
print


It would be nice if class objects were *instances* in Python.  And it would
be nice if one could define a metaclass and then include it as part of a
class definition along the lines of:

class Class2(Class1) Class2Meta:

that is to say,

class a-class-name(list-of-base-classes) a-metaclass-instance:

or something similar.


Jim






More information about the Python-list mailing list