如果某个对象不可删除,则询问它是否为字典密钥是错误的

时间:2017-04-28 19:10:09

标签: python dictionary

如果一个对象不可用,就像一个集合一样,那么询问它是否是字典的键是错误的:

>>> key = set()
>>> key in {}
Traceback (most recent call last):
TypeError: unhashable type: 'set'

我只是想知道为什么会这样。

编辑:我知道该对象需要进行哈希处理,但不能解释为什么如果它不能成为错误的原因。

4 个答案:

答案 0 :(得分:2)

那是因为Python需要做的第一件事是检查它是否在dict中试图散列对象。

作为一个替代设计决策,Python可能已经处理了这个案例;它将在dict.__contains__实施中完成,捕获TypeError并返回False。但是,这为用户提供的信息少于仅仅保留未处理的异常,因此可以说 更有用。

答案 1 :(得分:1)

这是因为检查字典成员资格要求首先散列对象,然后使用__eq__进行比较。

考虑以下玩具示例:

class Key(object):
  def __hash__(self):
    print('calling hash')
    return 1

  def __eq__(self, other):
    print('calling eq')
    return True

dct = {1: 2}
Key() in dct
# calling hash
# calling eq

在您的示例中,set()未超过哈希阶段,并且正确引发错误,而不是以静默方式传递。

答案 2 :(得分:0)

将一个集合作为字典中的一个键是iffy因为set是一个可变数据结构。

当您向集合中添加一些内容时会发生什么?应该' a'继续被视为字典中的一个关键字,还是应该将其他一些具有原始价值的集合视为关键字?

要使用类似于集合的结构作为键,请使用不可变的'冻结集'代替。实例化后,你无法更改冻结集的值,因此python可以安全地允许它成为dict中的密钥。

答案 3 :(得分:0)

这种方式更有用。

key in d应该等同于

any(key == k for k in d)
对于不可用的对象,

实际上可以为True:

d = {frozenset(): 1}
key = set()
print(any(key == k for k in d)) # prints True

因此,尝试始终为不可用的对象返回False会产生不一致。从理论上讲,我们可以尝试回退到for循环,但这会导致静默的O(len(d))性能下降,并且会失去dict的好处,但收益微乎其微。

此外,即使key in d对于不可用的False总是key,或者如果它有某种后退,大多数甚至发生此类测试的情况仍可能是错误而不是故意。异常比False结果更有用。