关于字典:为什么Python字典不能在此脚本中独立处理密钥?

Why don't Python dictionaries treat keys independently in this script?

本问题已经有最佳答案,请猛点这里访问。

我期待着我的挫折会被一些启示所覆盖-下面是一个演示这个问题的脚本的最小版本:

首先,我创建一个字典:

1
2
3
4
dic  = {
    'foo':{},
    'bar':{}
    }

然后我们实例化一个模板字典,它可以被迭代附加至dic键:

1
2
3
appendic= {  
    'is':'',       #  '' is a terminal value to be replaced later
}

在这里,我们将EDOCX1[1]附加到EDOCX1[0]中的每个键:

1
2
dic['foo'] = appendic
dic['bar'] = appendic

现在,我们用一些有意义的东西替换终端值"":

1
2
dic['foo']['is'] = 'foo'
dic['bar']['is'] = 'bar'

此时,我的直觉告诉我,如果我们调用:

print(dic['foo']['is'])我们得到'foo'

但相反,python返回'bar'…对我未经训练的头脑来说,这是违反直觉的。

问题:

  • 如何告诉python保持dic的键独立?
  • 为什么这是默认行为?这有什么用例?

当您将一个appendic分配给两个不同的键时,python不会进行复制。它分配一个引用。

因此,dic['please_make_me_Foo']dic['dont_make_him_Bar']都指同一对象。这些字典不是单独的,它们都是同一个对象,其中一个appendic也引用了。

如果您希望这些字典是单独的,请创建一个appendic的副本。dict.copy()方法创建字典的浅显副本:

1
2
dic['please_make_me_Foo']= appendic.copy()
dic['dont_make_him_Bar'] = appendic.copy()

Shallow意味着创建了一个新字典,并且复制了对所包含键和值的所有引用。

如果appendic本身包含的值也是字典,则不会复制这些值。新拷贝和appendic都引用相同的值。在大多数情况下,这不是问题,因为大多数基值(字符串、整数等)都是不可变的,并且在用新值替换这些值时,您永远不会注意到引用是共享的。


你做口述:

1
2
3
appendic= {  
    'Python_made_me':''
}

把它加到你的另一个口述里两次

1
2
dic['please_make_me_Foo']= appendic
dic['dont_make_him_Bar'] = appendic

将单个dict的Python_made_me值设置两次

1
2
dic['please_make_me_Foo']['Python_made_me'] = 'Foo'
dic['dont_make_him_Bar']['Python_made_me']  = 'Bar'

但是因为它们是同一个dict,所以第二行覆盖了第一行

如果需要复制,需要使用copy方法:

1
2
dic['please_make_me_Foo']= appendic.copy()
dic['dont_make_him_Bar'] = appendic.copy()


好的,我只写这个作为对其他答案的补充。当您操纵字典时,您操纵对实例的引用,这是错误的根本原因。使用hex(id(foo))可以得到foo的内存地址,因此让我们在下面的示例中显示d实例的地址,使其成为有形的:

1
2
3
4
5
6
>>> hex(id(d))
'0x10bd95e60'
>>> hex(id(e[1]))
'0x10bd95e60'
>>> hex(id(f[1]))
'0x10bd95e60'

因此,如果您从e[1]中添加或删除值,实际上您更改的实例与d所指的实例相同,并且字典是可变的,也就是说,您可以在中更改值。

现在你想知道,为什么处理整数时不会发生这种情况?因为,事实上,整数是不可变的:

1
2
3
4
5
6
7
8
9
>>> i = 1
>>> hex(id(i))
'0x10ba51e90'
>>> j = i
>>> hex(id(j))
'0x10ba51e90'
>>> i = 2
>>> hex(id(i))
'0x10ba51eb0'

也就是说,我指的是记忆中的另一个地方。

但是,可以通过使用类来创建可变整数:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
>>> class Integer:
...   def __init__(self, i):
...     self.i = i
...
>>> i = Integer(2)
>>> hex(id(i))
'0x10bd9b410'
>>> j = i
>>> hex(id(j))
'0x10bd9b410'
>>> j.i = 2
>>> i.i
2
>>> hex(id(i))
'0x10bd9b410'

要创建同一词典的新实例,需要使用dict的copy()成员:

1
2
3
4
5
6
7
8
9
10
11
>>> hex(id(d))
'0x10bd95e60'
>>> w = d.copy()
>>> x = d.copy()
>>> y = d.copy()
>>> hex(id(w))
'0x10bd96128'
>>> hex(id(x))
'0x10bd95f80'
>>> hex(id(y))
'0x10bd96098'


1
2
dic['please_make_me_Foo']= appendic
dic['dont_make_him_Bar'] = appendic

appendic是一个对象—您将对同一对象的引用分配给dic中的两个键。所以当你改变一个,你就改变了两个。

试试这个:

1
2
dic['please_make_me_Foo']= appendic.copy()
dic['dont_make_him_Bar'] = appendic.copy()