Python Cookbook question re 5.6

Bengt Richter bokr at oz.net
Sun Dec 14 13:50:07 EST 2003


On Mon, 15 Dec 2003 08:51:07 -0800, Scott David Daniels <Scott.Daniels at Acm.Org> wrote:

>Joe wrote:
>> The recipe in question is "Implementing Static Methods".  It shows how to
>> use staticmethod().  This sentence in the Discussion section isn't clear to
>> me:  "An attribute of a class object that starts out as a Python function
>> implicitly mutates into an unbound method."  I'm not sure what this means,
>> exactly.  Can anyone elaborate?
>> 
>> Thanks,
>> Chris
>
>First, you might cite the actual recipe here, so people can go look:
>
>
>The way a class is constructed consists of:
>  1) open a scope at the point where the class definition starts.
>  2) Collect each definition in the scope (value an name).  At this
>     point (during the collection), you are building "normal"
>     functions with def.
>  3) When the end of the class definition is found, all of the
>     definitions collected in (2), along with the class name and
>     superclasses are used to build the actual class.  This is the
>     moment when the normal functions created in step 2 are used
>     to build "unbound methods" -- the magic used to make objects
>     work.
UIAM that is not quite accurate. The defined "normal functions" you mention
remain so until dynamically accessed as attributes of the relevant class. If you bypass the
getattr mechanism (e.g., looking in the class dict), you find that the functions are still "normal":

 >>> class C(object):
 ...     def meth(*args): print 'meth args:', args
 ...
 >>> c = C()
 >>> C.meth
 <unbound method C.meth>
 >>> c.meth
 <bound method C.meth of <__main__.C object at 0x00902410>>

but this way you see the plain old function:
 >>> C.__dict__['meth']
 <function meth at 0x009050B0>

if you use getattr, you can see the attribute magic:
 >>> getattr(C,'meth')
 <unbound method C.meth>

and via an instance:
 >>> getattr(c,'meth')
 <bound method C.meth of <__main__.C object at 0x00902410>>

calling the plain function:
 >>> C.__dict__['meth'](1,2,3)
 meth args: (1, 2, 3)

trying the same as class attribute, which gets you the unbound method:
 >>> getattr(C,'meth')(1,2,3)
 Traceback (most recent call last):
   File "<stdin>", line 1, in ?
 TypeError: unbound method meth() must be called with C instance as first argument (got int insta
 nce instead)

you can pass the instance explicitly:
 >>> getattr(C,'meth')(c,1,2,3)
 meth args: (<__main__.C object at 0x00902410>, 1, 2, 3)

you can make a global binding to the plain function:
 >>> gm = C.__dict__['meth']
 >>> gm(1,2,3)
 meth args: (1, 2, 3)

you can add a method dynamically to the class:
 >>> C.m2 = lambda *args:args

and the getattr magic will do its thing:
 >>> C.m2
 <unbound method C.<lambda>>
 >>> c.m2
 <bound method C.<lambda> of <__main__.C object at 0x00902410>>
 >>> c.m2(1,2,3)
 (<__main__.C object at 0x00902410>, 1, 2, 3)
 >>> C.m2(1,2,3)
 Traceback (most recent call last):
   File "<stdin>", line 1, in ?
 TypeError: unbound method <lambda>() must be called with C instance as first argument (got int i
 nstance instead)
 >>> C.m2(c,1,2,3)
 (<__main__.C object at 0x00902410>, 1, 2, 3)

You can also define descriptors that will intercept the getattr magic and alter the behavior,
if you want to.

Regards,
Bengt Richter




More information about the Python-list mailing list