据我所知,python需要一个不可变的对象才能用作字典键。例如,我们无法使用list
作为字典键,我们必须先将它们转换为tuple
。因此,制作可变对象的副本并将副本用作密钥是错误的。这种方式对对象的更改不会影响密钥。是因为这种方法空间效率低下还是别的什么?
答案 0 :(得分:4)
dict
不能通过内部制作深层副本来接受可变对象,每个原因都是足够的。其中包括:
1)这会使插入的开销任意高。
2)这会使插入的开销变得不可预测。
3)它会破坏O(1)查找。
O(1)查找是通过散列密钥并使用该散列值作为表的索引来实现的。这预先假定密钥可以存在永久哈希。
在您的假设版本的Python中考虑这个程序:
d = { [1,2,3]:"hello" }
for k in d:
k.append(4)
我们正在修改dict的复制密钥对象。显然必须重新计算哈希值,同样显然必须重新计算d
的哈希表。但当?或者,为了拟人化,d
如何知道k
修改了自己?答案:实际上,它不能。
不,如果我们想要O(1)查找,我们需要一个哈希表。如果我们想要一个哈希表,我们需要密钥的永久哈希值。如果我们想要键的永久哈希值,我们需要不可变对象。
相反,如果我们想要可变密钥,我们可以实现O(logN)查找。但由于Python需要O(1)查找,我们有不可变的键。
答案 1 :(得分:1)
我认为一个例子将有助于理解:
my_value = 123456
my_string = 'key'
my_dict = {my_string : my_value}
my_string = 'not key anymore!'
print my_dict['key'] #prints : 123456
print my_dict[my_string] # raise KeyError
正如您所看到的,在修改“原始”对象后仍可以重新启用密钥。但我说“原始”,因为对象没有真正修改,但创建了一个新对象。那是不可改变的意思。 你可以在这里阅读:https://docs.python.org/2/reference/datamodel.html
如果密钥是可变的,那么当我更改对象本身时,密钥的值也会改变。
答案 2 :(得分:1)
因为Python不喜欢制作太多内容。将(key,value)项添加到dict时,dictionnary中的键和值引用到原始对象。
即使密钥是原始对象的副本,您仍然可以通过迭代items
来获取对它的引用。一旦你得到了这个参考,你就可以用所有相关的问题来改变它......
因此,复制方法要求dict
不仅在添加元素时获取密钥的副本,而且还始终在keys()
或items()
方法中返回新副本
最后但并非最不重要的是,由于存在可变类和非可变类,因此可以在Python中构建不可复制的类:
>>> class UnCopy(object):
def __new__(cls, x, y):
obj = super(UnCopy, cls).__new__(cls)
obj.x = x
obj.y = y
return obj
>>> c = UnCopy('a', 'b')
>>> d = copy.deepcopy(c)
Traceback (most recent call last):
File "<pyshell#20>", line 1, in <module>
d = copy.deepcopy(c)
File "C:\Python27\lib\copy.py", line 190, in deepcopy
y = _reconstruct(x, rv, 1, memo)
File "C:\Python27\lib\copy.py", line 329, in _reconstruct
y = callable(*args)
File "C:\Python27\lib\copy_reg.py", line 93, in __newobj__
return cls.__new__(cls, *args)
TypeError: __new__() takes exactly 3 arguments (1 given)
无论如何,唯一真正的原因是:它不是dict
类的行为方式,而是来自collection
模块的其他映射类。