Confused with classmethods

Bengt Richter bokr at oz.net
Sat Mar 12 18:33:37 EST 2005


On Fri, 11 Mar 2005 14:04:26 -0800, jfj <jfj at freemail.gr> wrote:

>Hi.
>
>Suppose this:
>
>########################
>
>def foo (x):
>     print x
>
>f = classmethod (foo)
>
>class A: pass
>
>a=A()
>a.f = f
>
>a.f()
># TypeError: 'classmethod' object is not callable!
>
>#######################
>
>I understand that this is a very peculiar use of
>classmethods but is this error intentional?
>Or did I completely missed the point somewhere?
>
Yes and yes ;-)

classmethod creates an an object that wraps the function you pass it.
The classmethod object has a __get__ method that gets control instead of the __get__
method of a function, when the object is found by way of attribute name
search looking in a new style class or base class. However, you created a 'classic'
class (A) instance a=A(), and the rules for looking up attributes on
classic class instances are different from the rules for newstyle class instances,
(though the difference does not come into play for your particular example ;-)

 >>> def foo(x): print x
 ...
 >>> f = classmethod(foo)
 >>> class A: pass
 ...
 >>> a=A()
 >>> a
 <__main__.A instance at 0x02EF142C>
 >>> a.f = f
 >>> a.f
 <classmethod object at 0x02E81374>

getattr(type(a), 'f') is checked first, but nothing is found. In that case,
a.f is simply retrieved with no special effects.

If you want to force the 'special effect' you could do it like:

 >>> a.f.__get__(None, A)
 <bound method classobj.foo of <class __main__.A at 0x02EE792C>>

Now the result of that _is_ callable:

 >>> a.f.__get__(None, A)()
 __main__.A

The normal way is to make the method callable accessible as an attribute of the class:

 >>> A.f = f
 >>> a.f
 <classmethod object at 0x02E81374>
The object is still retrieved from the instance (and would be for newstyle instance as well,
though you can create a descriptor other than classmethod than can prevent this).

 >>> a.f()
 Traceback (most recent call last):
   File "<stdin>", line 1, in ?
 TypeError: 'classmethod' object is not callable

It's not callable, because to become a callable, its __get__ method has to be called,
which will return the actual callable -- which will not be the function itself, but
a callable that holds a reference to both the function and the thing it's "bound" to
to form a bound method of one kind or another.

If we delete the instance attribute, the search for 'f' will go to the instance's class:

 >>> del a.f
 >>> a.f
 <bound method classobj.foo of <class __main__.A at 0x02EE792C>>

and the automated magic happens, and what is returned _is_ callable:
 >>> a.f()
 __main__.A

If you want to delve into it, read
http://www.python.org/2.2.2/descrintro.html
and associated info.

If you wanted to make plain foo a bound method of A, you could do it manually:

 >>> foo.__get__(a, A)
 <bound method A.foo of <__main__.A instance at 0x02EF142C>>

which you can call, and it will print the first argument (named x instead of self in this case):
>>> foo.__get__(a, A)()
<__main__.A instance at 0x02EF142C>

If you pass None instead of the instance, you get an unbound method:
>>> foo.__get__(None, A)
<unbound method A.foo>

Which you can call with an instance:
 >>> foo.__get__(None, A)(a)
 <__main__.A instance at 0x02EF142C>
or a different instance
 >>> foo.__get__(None, A)(A())
 <__main__.A instance at 0x02EF18CC>

If you don't pass the type, some things won't work fully, but the usual will
 >>> foo.__get__(a)
 <bound method ?.foo of <__main__.A instance at 0x02EF142C>>

As can be seen, the original function is in there in the bound method:

 >>> foo.__get__(a, A).im_func
 <function foo at 0x02EE8B54>

And you can call it as such if you like:
 >>> foo.__get__(a, A).im_func('hi, via plain function')
 hi, via plain function

Descriptors are a big deal in the new python. They are at the root
of much of the magic.

Regards,
Bengt Richter



More information about the Python-list mailing list