Confused with classmethods

Ruud de Jong ruud<dot>de<dot>jong<at>consunet <dot>
Fri Mar 11 12:11:12 EST 2005


jfj schreef:
> Diez B. Roggisch wrote:
> 
>>> I understand that this is a very peculiar use of
>>> classmethods but is this error intentional?
>>> Or did I completely missed the point somewhere?
>>
Note that this is not really related to classmethods. A similar
"problem" exists if you want to use an ordinary function as a method:

 >>> def g(self):
	print "argument: " + str(self)

 >>> class A(object):
	pass

 >>> a=A()
 >>> a.x = g
 >>> a.x
<function g at 0x00B9F070>

Here you see that none of the class magic has been performed; a.x
is *not* a method, but an ordinary function. It has absolutely no
relation to / knowledge of the class that a belongs to.
Calling a.x() leads to an exception:

 >>> a.x()

Traceback (most recent call last):
   File "<pyshell#53>", line 1, in -toplevel-
     a.x()
TypeError: g() takes exactly 1 argument (0 given)

And of course, if you supply an argument, everything works fine:

 >>> a.x(1)
argument: 1

To get the method behaviour the function needs to be associated
with the class:

 >>> del a.x
 >>> A.x = g
 >>> a.x
<bound method A.g of <__main__.A object at 0x00B93B90>>
 >>> a.x()
argument: <__main__.A object at 0x00B93B90>
 >>>

The mechanism is basically as follows (with thanks to Alex Martelli's
execellent Python in a Nutshell, Chapter 5). When a.x is being resolved,
first a.__dict__['x'] is tried. In the first case x was indeed defined 
in a.__dict__, and its value was returned (the function also known as 
g). No class magic at all. The same happened with your
classmethod foo: you get a bare classmethod, and apparently these
are not callable.
In the second case, a.__dict__ did not have a key 'x'. The lookup then
continues in a's class: A.__dict__['x']. Again it finds the function.
But when a class __dict__ lookup returns a function, the result is
first wrapped into a bound or unbound method object (depending on 
whether you arrived there via a.x or A.x).

So far for *how* it works. As to *why* it works like this, I don't know
for sure. But my guess is that the reasoning was something as follows:
if you define a function (regular or something special like a
classmethod) only for an instance of a class, you obviously don't
want to use it in a class context: it is -by definition- invisible to
the class, or to other instances of the same class.
One possible use case would be to store a callback function.
And in that case you definitely don't want the class magic to happen
when you reference the function.

Just my 2 cents

Regards,

Ruud

-- 
'@'.join('.'.join(s) for s in (['ruud','de','jong'],['tiscali','nl']))



More information about the Python-list mailing list