密钥不是python字典的唯一!

时间:2011-03-27 18:18:26

标签: python dictionary hashtable

这里有一个愚蠢的新手问题 对于python词典q len(set(q.keys())) != len(q.keys())。这甚至可能吗?

2 个答案:

答案 0 :(得分:16)

如果违反dict的要求并更改其哈希值,就会发生这种情况。

dict中使用对象时,其哈希值不得更改,并且其与其他对象的相等性不得更改。其他属性可能会更改,只要它们不会影响它对dict的显示方式。

(这 not 意味着永远不允许更改哈希值。这是一种常见的误解。哈希值本身可能会发生变化。只有dict要求密钥哈希值不可变,而不是__hash__本身。)

以下代码将一个对象添加到dict,然后从dict下面更改其散列。然后q[a] = 2添加a作为dict中的新键,即使它已经存在;由于哈希值已更改,因此dict未找到旧值。这再现了你所看到的特殊性。

class Test(object):
    def __init__(self, h):
        self.h = h
    def __hash__(self):
        return self.h

a = Test(1)
q = {}
q[a] = 1
a.h = 2
q[a] = 2

print q

# True:
print len(set(q.keys())) != len(q.keys())

答案 1 :(得分:1)

字典和集合的基础代码基本相同,因此您通常可以预期len(set(d.keys()) == len(d.keys())是不变的。

也就是说,set和dicts都依赖于__eq__和__hash__来识别唯一值并组织它们以进行有效搜索。因此,如果那些返回不一致的结果(或违反“a == b暗示hash(a)== hash(b)”的规则,则无法强制执行不变量:

>>> from random import randrange
>>> class A():
    def __init__(self, x):
        self.x = x
    def __eq__(self, other):
        return bool(randrange(2))
    def __hash__(self):
        return randrange(8)
    def __repr__(self):
        return '|%d|' % self.x


>>> s = [A(i) for i in range(100)]
>>> d = dict.fromkeys(s)
>>> len(d.keys())
29
>>> len(set(d.keys()))
12