如果一个对象不可用,就像一个集合一样,那么询问它是否是字典的键是错误的:
>>> key = set()
>>> key in {}
Traceback (most recent call last):
TypeError: unhashable type: 'set'
我只是想知道为什么会这样。
编辑:我知道该对象需要进行哈希处理,但不能解释为什么如果它不能成为错误的原因。
答案 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
结果更有用。