为什么两级字典的值都指向Python 2.7中的同一个对象?

Tom*_*ith 2 python dictionary list append python-2.7

我试图定义一个函数来创建一个双层字典,所以它应该生成格式

dict = {tier1:{tier2:value}}.
Run Code Online (Sandbox Code Playgroud)

代码是:

def two_tier_dict_init(tier1,tier2,value):
    dict_name = {}
    for t1 in tier1:
        dict_name[t1] = {}
        for t2 in tier2:
            dict_name[t1][t2] = value
    return dict_name
Run Code Online (Sandbox Code Playgroud)

所以下面的例子......

tier1 = ["foo","bar"]
tier2 = ["x","y"]
value = []
foobar_dict = two_tier_dict_init(tier1,tier2,value)
Run Code Online (Sandbox Code Playgroud)

从它的表面产生我想要的东西:

foobar_dict =  {'foo':{'x': [],'y':[]},
                'bar':{'x': [],'y':[]}}                   }
Run Code Online (Sandbox Code Playgroud)

但是,当附加任何值时

foobar_dict["foo"]["x"].append("thing")
Run Code Online (Sandbox Code Playgroud)

所有值都会被追加,结果如下:

foobar_dict =  {'foo':{'x': ["thing"],'y':["thing"]},
                'bar':{'x': ["thing"],'y':["thing"]}}
Run Code Online (Sandbox Code Playgroud)

起初我假设由于我的定义构建字典的方式,所有值都指向内存中的相同空间,但我无法弄清楚为什么会出现这种情况.然后我发现如果我将值从空列表更改为整数,当我执行以下操作时,

foobar_dict["foo"]["x"] +=1
Run Code Online (Sandbox Code Playgroud)

只更改所需的值.

因此,我必须得出结论,这与该list.append方法有关,但我无法弄明白.解释是什么?

注意我需要这个函数来构建大型字典词典,其中每个层都有数百个元素.我也使用相同的方法来构建一个出现相同问题的三层版本.

Mar*_*ers 6

您只传入一个列表对象,而您的第二层字典仅存储对该一个对象的引用.

如果需要存储不同的列表,则需要为每个条目创建一个新列表.您可以使用工厂函数:

def two_tier_dict_init(tier1, tier2, value_factory):
    dict_name = {}
    for t1 in tier1:
        dict_name[t1] = {}
        for t2 in tier2:
            dict_name[t1][t2] = value_factory()
    return dict_name
Run Code Online (Sandbox Code Playgroud)

然后使用:

two_tier_dict_init(tier1, tier2, list)
Run Code Online (Sandbox Code Playgroud)

让它创建空列表.您可以在此处使用任何可调用值作为值工厂,包括lambda如果要存储不可变对象(如字符串或整数):

two_tier_dict_init(tier1, tier2, lambda: "I am shared but immutable")
Run Code Online (Sandbox Code Playgroud)

您可以使用dict理解来简化您的功能:

def two_tier_dict_init(tier1, tier2, value_factory):
    return {t1: {t2: value_factory() for t2 in tier2} for t1 in tier1}
Run Code Online (Sandbox Code Playgroud)