Anomaly in creating class methods

Bengt Richter bokr at oz.net
Thu Nov 3 14:01:51 EST 2005


On 3 Nov 2005 03:19:22 -0800, "venk" <venkatasubramanian at gmail.com> wrote:

>Hi,
> given below is my interaction with the interpreter.... In one case, i
>have created the class method using the "famous idiom"... and in the
>other, i have tried to create it outside the class definition... why
>isn't the latter working ? (of course, the presence of decorators is a
>different issue)....
>>>> class D:
>...     def f(cls):
>...             print cls
>...
>>>> D.f=classmethod(D.f)
>>>> D.f
><bound method classobj.f of <class __main__.D at 0xb7c99f5c>>
>>>> D.f()
>Traceback (most recent call last):
>  File "<stdin>", line 1, in ?
>TypeError: unbound method f() must be called with D instance as first
>argument (got classobj instance instead)
>>>> class D:
>...     def f(cls):
>...             print cls
>...     f=classmethod(f)
>...
>>>> D.f
><bound method classobj.f of <class __main__.D at 0xb7c9947c>>
>>>> D.f()
>__main__.D
>

I think you are on very thin ice using classmethod for old-style classes.
In any case, you are not providing the same argument to classmethod
in the two examples above. I'm not sure what classmethod tries to do with
an unbound method, but it's not normal usage even in newstyle classes.
It probably just saves the callable argument as an attribute of the
classmethod instance, so the problem doesn't show until the descriptor
machinery comes into play (on attribute access and calling the bound callable).


 >>> def showcmarg(f): print 'cmarg=%r'%f; return classmethod(f)
 ...
 >>> class D:
 ...     def f(cls): print cls
 ...
 >>> D.f=showcmarg(D.f)
 cmarg=<unbound method D.f>

Note that that was an unbound method as the arg, that gets stored in the descriptor

 >>> D.f
 <bound method classobj.f of <class __main__.D at 0x02EE9A1C>>
 >>> D.f()
 Traceback (most recent call last):
   File "<stdin>", line 1, in ?
 TypeError: unbound method f() must be called with D instance as first argument (got classobj
 tance instead)

That's just the callable (D unbound method) you gave it complaining that the descriptor is
passing the intended cls D (a classobj) instead of an instance of D to the unbound method.

 >>> class D:
 ...     def f(cls): print cls
 ...     f = showcmarg(f)
 ...
 cmarg=<function f at 0x02EEAE2C>

Note that this time that was a _function_ arg, because it was picked up as a local
binding during the execution of the body of the class definition, where classmethod
was called. The function will have no demands except to match its signature when the
classmethod descriptor instance calls it with first arg of D.

 >>> D.f
 <bound method classobj.f of <class __main__.D at 0x02EE9A7C>>
 >>> D.f()
 __main__.D

You could extract the normal (function) classmethod arg from the outside though:

 >>> class D:
 ...     def f(cls): print cls
 ...
 >>> D.f=showcmarg(D.f.im_func)
 cmarg=<function f at 0x02EEAD84>
 >>> D.f
 <bound method classobj.f of <class __main__.D at 0x02EE9A4C>>
 >>> D.f()
 __main__.D

Suggest migrating to new style classes consistently ;-)

Regards,
Bengt Richter



More information about the Python-list mailing list