Weird lambda behavior

Rüdiger Ranft _rdi_ at web.de
Wed Apr 22 14:52:47 CEST 2009


Chris Rebert schrieb:
> On Wed, Apr 22, 2009 at 4:50 AM, Rüdiger Ranft <_rdi_ at web.de> wrote:
>> Hi all,
>>
>> I want to generate some methods in a class using setattr and lambda.
>> Within each generated function a name parameter to the function is
>> replaced by a string constant, to keep trail which function was called.
>> The problem I have is, that the substituted name parameter is not
>> replaced by the correct name of the function, but by the last name the
>> for loop has seen.
>>
>> import unittest
>>
>> class WidgetDummy:
>>    '''This class records all calls to methods to an outer list'''
>>
>>    def __init__( self, name, calls ):
>>        '''name is the name of the object, which gets included into a
>>        call record. calls is the list where the calls are appended.'''
>>        self.name =ame
>>        self.calls =alls
>>        for fn in ( 'Clear', 'Append', 'foobar' ):
>>            func =ambda *y,**z: self.__callFn__( fn, y, z )
>>            setattr( self, fn, func )
> 
> Common wart to run into as of late. fn (in the lambda) doesn't get
> evaluated until the call-time of the lambda, by which point the loop
> has finished and the loop variable has been changed to its final
> value, which is used by the lambda.

Doh! I wasn't aware of the point when the lambda expression is evaluated.

> Workaround:
> #exact syntax may vary with your version of Python, but you should be
> able to get the idea
> func =ambda *y,**z, fn=fn: self.__callFn__( fn, y, z )
> 
> The default argument value gets evaluated at definition-time, thus
> forcing the right value of fn within the function.

Thank you, I replaced the direct assignment with a proxy function call,
and now it works.

    def __init__( self, name, calls ):
        self.name = name
        self.calls = calls

        def forceEval(x):
            setattr( self, fn, lambda *y,**z: self.__callFn__(x, y, z) )

        for fn in ( 'Clear', 'Append', 'foobar' ):
            forceEval(fn)

-- 
GPG encrypted mails preferred.
GPG verschlüsselte Mails bevorzugt.
---> http://chaosradio.ccc.de/media/ds/ds085.pdf Seite 20 <----




More information about the Python-list mailing list