Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Defining a python dictionary of lambdas through a for cycle [duplicate]

I need to define a dictionary of python lambda functions through a for cycle. Each lambda function needs the value of the relative dictionary key to work, but my hope is to avoid to pass such key as an argument. Here is a dummy version of my problem.

Given

a = {'bar': 0, 'foo': 1}  # a reference dictionary 

dic1 = {'bar': lambda x: x['bar'], 'foo': lambda x: x['foo']}
dic2 = {key: lambda x: x[key] for key in a}

I expect dic1 and dic2 to do the same thing, however only dic1 behaves as I would want to. In particular, the result of

print(dic1['bar'](a), dic1['foo'](a))
print(dic2['bar'](a), dic2['foo'](a))

is

0 1
1 1

instead of

0 1
0 1
like image 335
ruggero.devita Avatar asked Jul 12 '19 14:07

ruggero.devita


People also ask

Can you use a for loop in a dictionary Python?

You can loop through a dictionary by using a for loop. When looping through a dictionary, the return value are the keys of the dictionary, but there are methods to return the values as well.

Can lambda function be reused in Python?

Can we reuse the lambda function? I guess the answer is yes because in the below example I can reuse the same lambda function to add the number to the existing numbers.

Can different forms of lambda take multiple Python statements expressions?

You cannot write multiple statements in the body of a lambda function. 4. We call the function and print the returned value.

How does Python define lambda expressions?

In Python, a lambda function is a single-line function declared with no name, which can have any number of arguments, but it can only have one expression. Such a function is capable of behaving similarly to a regular function declared using the Python's def keyword.


2 Answers

The reason it doesn't work has to do with late-binding closures in Python.

Python’s closures are late binding. This means that the values of variables used in closures are looked up at the time the inner function is called.

Adapting the source explanation, whenever the return function is called, the value of key is looked up in the surrounding scope at call time. By then, the loop has completed and key is left with its final value of 'foo'.

You can resolve this by creating a closure that binds immediately to its arguments by using a default arg like so:

a = {'bar': 0, 'foo': 1}  # a reference dictionary 

dic1 = {'bar': lambda x: x['bar'], 'foo': lambda x: x['foo']}
dic2 = {key: lambda x, key=key: x[key] for key in a}

print(dic1['bar'](a), dic1['foo'](a))
print(dic2['bar'](a), dic2['foo'](a))

Result:

0 1
0 1
like image 150
TomNash Avatar answered Oct 22 '22 10:10

TomNash


One simple way to do that is with operator.itemgetter:

from operator import itemgetter

a = {'bar': 0, 'foo': 1}
dic1 = {key: itemgetter(key) for key in a}
print(dic1['bar'](a), dic1['foo'](a))
# 0 1

Alternatively, you need to bind each value of key to each dictionary value, typically you do that with something like this:

a = {'bar': 0, 'foo': 1}
dic1 = {key: (lambda key: lambda x: x[key])(key) for key in a}
print(dic1['bar'](a), dic1['foo'](a))
# 0 1
like image 34
jdehesa Avatar answered Oct 22 '22 10:10

jdehesa