Scope of lambda functions and their parameters?

后端 未结 10 980
隐瞒了意图╮
隐瞒了意图╮ 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:22

    First, what you are seeing is not a problem, and not related to call-by-reference or by-value.

    The lambda syntax you defined has no parameters, and as such, the scope you are seeing with parameter m is external to the lambda function. This is why you are seeing these results.

    Lambda syntax, in your example is not necessary, and you would rather be using a simple function call:

    for m in ('do', 're', 'mi'):
        callback(m)
    

    Again, you should be very precise about what lambda parameters you are using and where exactly their scope begins and ends.

    As a side note, regarding parameter passing. Parameters in python are always references to objects. To quote Alex Martelli:

    The terminology problem may be due to the fact that, in python, the value of a name is a reference to an object. So, you always pass the value (no implicit copying), and that value is always a reference. [...] Now if you want to coin a name for that, such as "by object reference", "by uncopied value", or whatever, be my guest. Trying to reuse terminology that is more generally applied to languages where "variables are boxes" to a language where "variables are post-it tags" is, IMHO, more likely to confuse than to help.

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

    there are actually no variables in the classic sense in Python, just names that have been bound by references to the applicable object. Even functions are some sort of object in Python, and lambdas do not make an exception to the rule :)

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

    As a side note, map, although despised by some well known Python figure, forces a construction which prevents this pitfall.

    fs = map (lambda i: lambda: callback (i), ['do', 're', 'mi'])
    

    NB : the first lambda i acts like the factory in other answers.

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

    The variable m is being captured, so your lambda expression always sees its "current" value.

    If you need to effectively capture the value at a moment in time, write a function takes the value you want as a parameter, and returns a lambda expression. At that point, the lambda will capture the parameter's value, which won't change when you call the function multiple times:

    def callback(msg):
        print msg
    
    def createCallback(msg):
        return lambda: callback(msg)
    
    #creating a list of function handles with an iterator
    funcList=[]
    for m in ('do', 're', 'mi'):
        funcList.append(createCallback(m))
    for f in funcList:
        f()
    

    Output:

    do
    re
    mi
    
    0 讨论(0)
  • 2020-11-22 13:30

    Not directly related to the issue at hand, but an invaluable piece of wisdom nevertheless: Python Objects by Fredrik Lundh.

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

    When a lambda is created, it doesn't make a copy of the variables in the enclosing scope that it uses. It maintains a reference to the environment so that it can look up the value of the variable later. There is just one m. It gets assigned to every time through the loop. After the loop, the variable m has value 'mi'. So when you actually run the function you created later, it will look up the value of m in the environment that created it, which will by then have value 'mi'.

    One common and idiomatic solution to this problem is to capture the value of m at the time that the lambda is created by using it as the default argument of an optional parameter. You usually use a parameter of the same name so you don't have to change the body of the code:

    for m in ('do', 're', 'mi'):
        funcList.append(lambda m=m: callback(m))
    
    0 讨论(0)
提交回复
热议问题