anonymous function objects?

Bengt Richter bokr at oz.net
Fri Apr 29 08:16:28 EDT 2005


On Thu, 28 Apr 2005 20:57:51 -0400, Peter Hansen <peter at engcorp.com> wrote:

>Uwe Mayer wrote:
>> Unfortunately I want to assign a handler function to an object and something
>> like this does not work:
>> 
>>>>>class Foobar(object): pass
>> 
>> ...
>> 
>>>>>a = Foobar()
>>>>>def a.handler():
>> 
>>   File "<stdin>", line 1
>>     def a.handler():
>>          ^
>> SyntaxError: invalid syntax
>
>But this does work, or something close to it:
>
> >>> class Foobar(object): pass
>...
> >>> def handler(self):
>...   print 'in handler'
>...
> >>> f = Foobar()
> >>> f.handler()
>Traceback (most recent call last):
>   File "<stdin>", line 1, in ?
>AttributeError: 'Foobar' object has no attribute 'handler'
> >>>
> >>> import new
> >>> f.handler = new.instancemethod(handler, f)
> >>> f.handler()
>in handler
>

Or you can doctor up a Foobar that treats all
function attributes of its instances as methods
to be bound to the specific instance, e.g.,
(not tested beyond what you see)

 >>> class Foobar(object):
 ...     def __getattribute__(self, attr):
 ...         sentinel = object()
 ...         atto = object.__getattribute__(self, '__dict__').get(attr, sentinel)
 ...         if hasattr(atto, '__get__'): return atto.__get__(self, type(self))
 ...         elif atto is sentinel: return object.__getattribute__(self, attr)
 ...         return atto
 ...
 >>> f = Foobar()
 >>> def handler(self): print 'handler of:', self
 ...
 >>> f.handler = handler
 >>> f.handler()
 handler of: <__main__.Foobar object at 0x02F0428C>
 >>> f
 <__main__.Foobar object at 0x02F0428C>
 >>> f.handler
 <bound method Foobar.handler of <__main__.Foobar object at 0x02F0428C>>
 >>> f.h 
 Traceback (most recent call last):
   File "<stdin>", line 1, in ?
   File "<stdin>", line 6, in __getattribute__
 AttributeError: 'Foobar' object has no attribute 'h'
 >>> f.h =123
 >>> f.h
 123
 >>> f.f2 = lambda self: 'Hi'
 >>> f.f2
 <bound method Foobar.<lambda> of <__main__.Foobar object at 0x02F0428C>>
 >>> f.f2()
 'Hi'
 >>> Foobar.ordinary_method = lambda self: 'ordinary'
 >>> f.ordinary_method
 <bound method Foobar.<lambda> of <__main__.Foobar object at 0x02F0428C>>
 >>> f.ordinary_method()
 'ordinary'

Try another Foobar instance:

 >>> g = Foobar()
 >>> g.ordinary_method()
 'ordinary'
 >>> g.handler
 Traceback (most recent call last):
   File "<stdin>", line 1, in ?
   File "<stdin>", line 6, in __getattribute__
 AttributeError: 'Foobar' object has no attribute 'handler'
 >>> def g_handler(self): print "g's own handler for", self
 ...
 >>> g.alias = g_handler
 >>> g.alias()
 g's own handler for <__main__.Foobar object at 0x02EF134C>

BTW new.instancemethod(handler, f) seems to duplicate handler.__get__(f),
leaving out the type(f) in handler.__get__(f, type(f)) that would make a bound method,
which is what it is, and which could be set as attribute of anything.

Note the difference between newh which sticks with the f
passed to new.instancemethod, and real, which Foobar.__getattribute__
dynamically attaches to the selected instance:

 >>> import new
 >>> f.newh = g.newh = new.instancemethod(lambda self:('newh', self), f)
 >>> f.real = g.real = lambda self:('real', self)
 >>> f.newh()
 ('newh', <__main__.Foobar object at 0x02F0428C>)
 >>> g.newh()
 ('newh', <__main__.Foobar object at 0x02F0428C>)
 >>> f.real()
 ('real', <__main__.Foobar object at 0x02F0428C>)
 >>> g.real()
 ('real', <__main__.Foobar object at 0x02EF134C>)
                                     ^^^^^^^^^^

IOW, a plain callable has __get__ and gets bound to the instance,
whereas a bound method doesn't have __get__ any more, so it gets
retrieved as a plain attribute. BTW, to get an ordinary function call
for a function attribute of a Foobar instance, you'd have to
eliminate the function's __get__ effect one way or another,
e.g. staticmethod (which really wraps the function with a descriptor,
substituting its own __get__, which Foobar.__getattribute__ uses transparently)

 >>> def sm(firstarg='sm first arg', *args): return firstarg, args
 ...
 >>> g.sm = staticmethod(sm)
 >>> g.sm()
 ('sm first arg', ())
 >>> g.sm
 <function sm at 0x02EE8E9C>
 >>> g.__dict__['sm']
 <staticmethod object at 0x02E8177C>
 >>> g.__dict__['sm'].__get__
 <method-wrapper object at 0x02F0476C>
 >>> g.__dict__['sm'].__get__(g)
 <function sm at 0x02EE8E9C>

Regards,
Bengt Richter



More information about the Python-list mailing list