为啥 Python 字典不在此脚本中独立处理键? [复制]
Posted
技术标签:
【中文标题】为啥 Python 字典不在此脚本中独立处理键? [复制]【英文标题】:Why don't Python dictionaries treat keys independently in this script? [duplicate]为什么 Python 字典不在此脚本中独立处理键? [复制] 【发布时间】:2014-06-26 03:13:55 【问题描述】:我希望我的挫败感会被一些启发所取代 - 这是演示问题的脚本的最小版本:
首先我创建一个字典:
dic =
'foo':,
'bar':
然后我们实例化一个可以迭代追加的模板字典
dic
的键:
appendic=
'is':'', # '' is a terminal value to be replaced later
所以这里我们将appendic
附加到dic
中的每个键:
dic['foo'] = appendic
dic['bar'] = appendic
现在我们用有意义的东西替换终端值'':
dic['foo']['is'] = 'foo'
dic['bar']['is'] = 'bar'
此时,我的直觉告诉我,如果我们调用:
print(dic['foo']['is'])
我们得到'foo'
但相反,Python 将 'bar'
... 返回给我未经训练的头脑,这是违反直觉的。
问题:
如何告诉 Python 保持 dic 的键独立? 为什么这是默认行为?这有什么用例?【问题讨论】:
【参考方案1】:当您将appendic
分配给两个不同的键时,Python 不会进行复制。而是分配一个引用。
因此,dic['please_make_me_Foo']
和dic['dont_make_him_Bar']
都引用了同一个对象。这些不是单独的字典,它们都是同一个对象,appendic
也是引用的对象。
如果您希望它们是单独的字典,请改为创建 appendic
的副本。 dict.copy()
方法创建字典的浅拷贝:
dic['please_make_me_Foo']= appendic.copy()
dic['dont_make_him_Bar'] = appendic.copy()
Shallow 意味着创建了一个新字典,并且复制了对所包含键和值的所有引用。
如果appendic
本身包含的值也是字典,则不会复制这些值。新副本和appendic
都将引用相同的值。在大多数情况下,这不是问题,因为大多数原始值(字符串、整数等)都是不可变的,并且当您将这些值替换为新值时,您永远不会注意到引用是共享的。
【讨论】:
@hello_there_andy:如果 Python 在您每次存储对它的引用时都会复制该对象,那么对于初学者来说,您将需要更多的内存来运行您的代码。 这必须是至少一百个问题的副本,您应该知道这些问题,因为您可能回答了其中的一半。为什么不关闭这个或至少下一个作为副本呢?一旦我回到电脑而不是移动设备上,我什至会帮助您寻找合适的... @hello_there_andy:这就是 Python 中赋值的工作方式。既然你很惊讶,我建议阅读 Ned Batchelder 的 Facts and myths about Python names and values。 @l4mpi:我很想找到一个合适的。我们真的需要一个更通用的“停止我的字典/列表/设置/自定义类正在共享”作为欺骗目标的某个地方。 @hello_there_andy:我们没想到你会找到骗子;当您还不了解 Python 存储对对象的引用时,肯定很难搜索该行为。 l4mpi 指的是他觉得我应该更了解并发现你是个骗子。 :-)【参考方案2】:你做一个字典:
appendic=
'Python_made_me':''
将它添加到您的其他字典两次
dic['please_make_me_Foo']= appendic
dic['dont_make_him_Bar'] = appendic
并设置单个dict的Python_made_me
值两次
dic['please_make_me_Foo']['Python_made_me'] = 'Foo'
dic['dont_make_him_Bar']['Python_made_me'] = 'Bar'
但是因为它们是相同的字典,第二行会覆盖第一行
如果需要复制,需要使用copy
方法:
dic['please_make_me_Foo']= appendic.copy()
dic['dont_make_him_Bar'] = appendic.copy()
【讨论】:
【参考方案3】:好的,我只是将其写为对其他答案的补充。当您操作字典时,您将 reference 操作到一个实例,这是您错误的根本原因。使用hex(id(foo))
,您将获得foo
的内存地址,所以让我们在下面的示例中显示d
实例的地址以使该有形:
>>> hex(id(d))
'0x10bd95e60'
>>> hex(id(e[1]))
'0x10bd95e60'
>>> hex(id(f[1]))
'0x10bd95e60'
因此,如果您在e[1]
中添加或删除值,您实际上更改的实例 与d
所指的实例相同,并且字典是可变的,即您可以更改内的值。
现在您想知道为什么在处理整数时不会发生这种情况?因为,事实上确实如此,只是整数是不可变的:
>>> i = 1
>>> hex(id(i))
'0x10ba51e90'
>>> j = i
>>> hex(id(j))
'0x10ba51e90'
>>> i = 2
>>> hex(id(i))
'0x10ba51eb0'
即i 指向内存中的另一个地方。
虽然可以通过使用类来创建一个可变整数:
>>> 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'
为了创建同一个字典的新实例,您需要使用字典的copy()
成员:
>>> 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'
【讨论】:
【参考方案4】:dic['please_make_me_Foo']= appendic
dic['dont_make_him_Bar'] = appendic
appendic
是一个对象 - 您正在为 dic
中的两个键分配对同一对象的引用。所以当你改变一个,你就改变了两个。
试试这个:
dic['please_make_me_Foo']= appendic.copy()
dic['dont_make_him_Bar'] = appendic.copy()
【讨论】:
以上是关于为啥 Python 字典不在此脚本中独立处理键? [复制]的主要内容,如果未能解决你的问题,请参考以下文章