keeping a ref to a non-member function in a class
Peter Otten
__peter__ at web.de
Wed Aug 17 02:21:41 EDT 2005
Gregory Bond wrote:
> Thanks Peter, that's a big help.
You're welcome.
> I can solve my problem now, but I'm chasing this further in the name of
> education, because it seems there is some deep magic happening here that
> I don't understand.
Python resorts to deep magic only when it's inevitable, i. e. hardly
ever :-)
> It seems that applying staticfunction() (or perhaps assigning to the
> class object) is treated differently if it happens in the class
> defininition, to when it happens at "run time". (My previous attempts
> to get staticmember() to do the right thing were all "run time" like the
> examples below.)
There is no distinction between "run time" and "class definition time". The
class "body" is actually a function that is called when the module is
loaded. Its locals() are then used to create a type instance, i. e. the
class.
> Say I wanted to keep the need for "staticmember" hidden from subclasses,
> and do the staticmember() conversion in the base class methods. I've
> tried 2 ways of doing this (no subclassing here, just to keep it simple):
>
>> class B(object):
>> fn = foo
>> def try1(self):
>> print "B.fn is", type(B.fn)
>> B.fn = staticmethod(B.fn)
>> print "B try1"
>> print "B.fn is now", type(B.fn)
>> B.fn()
>>
>> def try2(self):
>> fn2 = staticmethod(B.fn)
>> print "B try2"
>> print "fn2 is now", type(fn2)
>> fn2()
>
> If I try method 1 (assigning to the class object - ignore for a moment
> the problem of only doing this once!) I get a set of very surprising
> results:
>
>> B.fn is <type 'instancemethod'>
>> B try1
>> B.fn is now <type 'instancemethod'>
>> Traceback (most recent call last):
>> File "t_o1.py", line 28, in ?
>> B().try1()
>> File "t_o1.py", line 17, in try1
>> B.fn()
>> TypeError: unbound method foo() must be called with B instance as first
>> argument (got nothing instead)
>
> note that assigning the staticmember() result to B.fn does NOT change
> the type of B.fn!! And foo is treated as a member function.
>
> So if I try method 2 (using staticmethod() at runtime):
>
>> B try2
>> fn2 is now <type 'staticmethod'>
>> Traceback (most recent call last):
>> File "t_o1.py", line 27, in ?
>> B().try2()
>> File "t_o1.py", line 22, in try2
>> fn2()
>> TypeError: 'staticmethod' object is not callable
>
> fn2 is a static method, as I'd expect, but it is somehow not callable?
Your problems stem from the fact that attribute assignment to a class
doesn't always roundtrip:
>>> A.value = 42
>>> A.value
42
>>> def f(): pass
...
>>> A.method = f
>>> A.method, f
(<unbound method A.f>, <function f at 0x4028eae4>)
If a class attribute is a descriptor i. e. it has a __get__() method (which
pure python functions do) A.method is not just a dictionary lookup but a
call
A.__dict__["method"].__get__(None, A)
This is slightly simplified as it doesn't take inheritance into account.
__get__() is free to apply whatever magic, but staticmethod just gives back
the original function whereas a some_func.__get__(None, SomeClass) gives
you an unbound method:
>>> staticmethod(f).__get__(None, A)
<function f at 0x4028eae4>
>>> f.__get__(None, A)
<unbound method A.f>
Is there a way to get the original function back out of the unbound method?
Let's see:
>>> dir(A.method)
['__call__', '__class__', '__cmp__', '__delattr__', '__doc__', '__get__',
'__getattribute__', '__hash__', '__init__', '__new__', '__reduce__',
'__reduce_ex__', '__repr__', '__setattr__', '__str__', 'im_class',
'im_func', 'im_self']
im_func seems promising:
>>> A.method.im_func
<function f at 0x4028eae4>
> Can someone explain what is going on here? Pointers to language spec,
> code, PEPs etc gladly accepted.
>
> Greg,
> caught in a twisty little maze of descriptors, all different!
The two relevant documents are
http://users.rcn.com/python/download/Descriptor.htm
http://www.python.org/2.2.1/descrintro.html
Peter
More information about the Python-list
mailing list