Dictionary With Lambda Values Updates All Entries

2019-02-26 06:04发布

问题:

I'm in Python 2.7. I have two classes and one namedtuple. One class houses a dictionary as an instance attribute and a function that assigns to that dictionary. (This is a very simplified version of the situation). The namedtuple is simple enough. The other class is one that adds entries into test_dict via the add_to_test_dict function call.

Then I instantiate DictManipulator and call the test function:

from collections import namedtuple


class DictHolder(object):
    def __init__(self):
        self.test_dict = {}
    def add_to_test_dict(self, key, val):
        self.test_dict[key] = val

TestTuple = namedtuple('TestTuple', 'name data')

class DictManipulator(object):
    def test(self):
        named_tuple_list = [TestTuple(name='key1', data=1), TestTuple(name='key2', data=1000)]
        self.my_dh = DictHolder()
        for item in named_tuple_list:
            self.my_dh.add_to_test_dict(item.name, lambda: item.data)

my_dm = DictManipulator()
my_dm.test()
print('key1 value: ', my_dm.my_dh.test_dict['key1']())
print('key2 value: ', my_dm.my_dh.test_dict['key2']())
# ('key1 value: ', 1000)
# ('key2 value: ', 1000)

Why do both keys return the same value there? I have experimented enough to say that the original named_tuple_list is not updated, and I've tried to use lambda: copy.deepcopy(item.data), but that doesn't work either. Thanks very much, folks.

回答1:

This is a typical late binding issue (see common gotchas): when the functions (being lambda/anonymous has nothing to do with it) are called, they access the current value of item, which is the last one from the loop. Try

lambda x=item: x.data 

in your loop instead. This works since default arguments are bound to a function at definition time while common local variables are evaluated at calling time.

Similar (possible duplicate) question: Python Lambda in a loop