Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Weird behaviour in python with dict of lambda functions

I have written a function for generating a dictionnary of lambda constant functions (it's part of a more complex function but I have simplified it to the code below):

def function(a):
    _interpolators = {}
    for key in a.keys():
        _interpolators[key] = lambda t: a[key]

    def _interpolate(t):
        return {key:f(t) for (key,f) in _interpolators.items()}
    return _interpolate

if __name__ == '__main__':    
    d = {"foo": 2, "bar":4}
    f = function(d)
    print(f(0)["bar"])

Using Python 3.5.1 on OSX El Capitan, the output of program is random. It can be 2or 4 while I would expect to have only 4. I don't quite understand this behaviour.

Note that the "bug" does not seem to occur with python 2.7.10 and I always get 4 when I run the program several times.

Is it some kind of bug in python 3.5.1 or did I miss something obvious ?

like image 702
Nicolas Rougier Avatar asked Apr 09 '26 06:04

Nicolas Rougier


1 Answers

The point is that in your lambda function which you have defined it as following :

lambda t: a[key]

You didn't used variable t and tried to get the a's value by passing the variable key as the key to your dictionary. And at the next time which you attempt to use this function in you enclosing function within dictionary comprehension:

{key:f(t) for (key,f) in _interpolators.items()}

Each time that you try to get the f(t), since the existence of t is redundant here python tries to get the a's value by passing the variable key to it, and it has nothing to do with t.

And the most important point is, that you expect to python override the key variable because you are using key in your dictionary comprehension.

And since lamda object is a function in python and has its own local namespace, each time that it tries to return the a[key] it cant access the iteration variable key inside the comprehension so due to LEGB manner after it (lambda) looks for key in Local namespace and then Enclosing and find nothing, it looks for it in Global scope.

And since the global key is the last iteration variable in following part :

for key in a.keys():
        _interpolators[key] = lambda t: a[key]

The lambda function will use it as the key. And since in python 3.X the order of dict.keys() is randomly, the result would be randomly as well. While it's not like so in python 2.

And for getting ride of this problem you can simply change your lambda as following:

lambda t: a[t]

And in your code :

def function(a):
    _interpolators = {}
    for key in a.keys():
        _interpolators[key] = lambda t: a[t]

    def _interpolate():
        return {k:f(k) for k,f in _interpolators.items()}
    return _interpolate

if __name__ == '__main__': 
    d = {"foo": 2, "bar":4}
    f = function(d)
    print(f()["bar"])
like image 66
Mazdak Avatar answered Apr 10 '26 20:04

Mazdak



Donate For Us

If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!