Scope of lambda functions and their parameters?

后端 未结 10 987
隐瞒了意图╮
隐瞒了意图╮ 2020-11-22 13:22

I need a callback function that is almost exactly the same for a series of gui events. The function will behave slightly differently depending on which event has called it.

相关标签:
10条回答
  • 2020-11-22 13:40

    the soluiton to lambda is more lambda

    In [0]: funcs = [(lambda j: (lambda: j))(i) for i in ('do', 're', 'mi')]
    
    In [1]: funcs
    Out[1]: 
    [<function __main__.<lambda>>,
     <function __main__.<lambda>>,
     <function __main__.<lambda>>]
    
    In [2]: [f() for f in funcs]
    Out[2]: ['do', 're', 'mi']
    

    the outer lambda is used to bind the current value of i to j at the

    each time the outer lambda is called it makes an instance of the inner lambda with j bound to the current value of i as i's value

    0 讨论(0)
  • 2020-11-22 13:41

    Python does uses references of course, but it does not matter in this context.

    When you define a lambda (or a function, since this is the exact same behavior), it does not evaluate the lambda expression before runtime:

    # defining that function is perfectly fine
    def broken():
        print undefined_var
    
    broken() # but calling it will raise a NameError
    

    Even more surprising than your lambda example:

    i = 'bar'
    def foo():
        print i
    
    foo() # bar
    
    i = 'banana'
    
    foo() # you would expect 'bar' here? well it prints 'banana'
    

    In short, think dynamic: nothing is evaluated before interpretation, that's why your code uses the latest value of m.

    When it looks for m in the lambda execution, m is taken from the topmost scope, which means that, as others pointed out; you can circumvent that problem by adding another scope:

    def factory(x):
        return lambda: callback(x)
    
    for m in ('do', 're', 'mi'):
        funcList.append(factory(m))
    

    Here, when the lambda is called, it looks in the lambda' definition scope for a x. This x is a local variable defined in factory's body. Because of this, the value used on lambda execution will be the value that was passed as a parameter during the call to factory. And doremi!

    As a note, I could have defined factory as factory(m) [replace x by m], the behavior is the same. I used a different name for clarity :)

    You might find that Andrej Bauer got similar lambda problems. What's interesting on that blog is the comments, where you'll learn more about python closure :)

    0 讨论(0)
  • 2020-11-22 13:44

    Yes, that's a problem of scope, it binds to the outer m, whether you are using a lambda or a local function. Instead, use a functor:

    class Func1(object):
        def __init__(self, callback, message):
            self.callback = callback
            self.message = message
        def __call__(self):
            return self.callback(self.message)
    funcList.append(Func1(callback, m))
    
    0 讨论(0)
  • 2020-11-22 13:49

    The problem here is the m variable (a reference) being taken from the surrounding scope. Only parameters are held in the lambda scope.

    To solve this you have to create another scope for lambda:

    def callback(msg):
        print msg
    
    def callback_factory(m):
        return lambda: callback(m)
    
    funcList=[]
    for m in ('do', 're', 'mi'):
        funcList.append(callback_factory(m))
    for f in funcList:
        f()
    

    In the example above, lambda also uses the surounding scope to find m, but this time it's callback_factory scope which is created once per every callback_factory call.

    Or with functools.partial:

    from functools import partial
    
    def callback(msg):
        print msg
    
    funcList=[partial(callback, m) for m in ('do', 're', 'mi')]
    for f in funcList:
        f()
    
    0 讨论(0)
提交回复
热议问题