class A(object):
x = 4
i = A()
d = {}
d[i] = 2
print d
i.x = 10
print d
我认为只有不可变对象可以是字典键,但上面的对象是可变的。
答案 0 :(得分:20)
具有__hash__方法的任何对象都可以是字典键。对于您编写的类,此方法默认返回基于id(self)的值,如果不通过这些类的标识确定相等性,您可能会惊讶于将它们用作键:
>>> class A(object):
... def __eq__(self, other):
... return True
...
>>> one, two = A(), A()
>>> d = {one: "one"}
>>> one == two
True
>>> d[one]
'one'
>>> d[two]
Traceback (most recent call last):
File "<stdin>", line 1, in <module>
KeyError: <__main__.A object at 0xb718836c>
>>> hash(set()) # sets cannot be dict keys
Traceback (most recent call last):
File "<stdin>", line 1, in <module>
TypeError: unhashable type: 'set'
在版本2.6中更改:__ hash__现在可以设置为None,以将类的实例显式标记为不可用。 [__hash__]
class Unhashable(object):
__hash__ = None
答案 1 :(得分:6)
要求是对象的散列不会随着时间的推移而改变,并且它会保持比较等于(==)与其原始值。您的A类符合这两个要求,因此它会生成有效的字典键。在键控时根本不考虑x属性,只有对象标识是。
答案 2 :(得分:6)
对象如果是hashable,则可以是字典中的键。
以下是文档中hashable的定义:
如果对象具有在其生命周期内永远不会更改的哈希值(它需要
__hash__()
方法),并且可以与其他对象进行比较(它需要__eq__()
或{{ 3}}方法)。比较相等的Hashable对象必须具有相同的哈希值。Hashability使对象可用作字典键和set成员,因为这些数据结构在内部使用哈希值。
所有Python的不可变内置对象都是可清除的,而没有可变容器(例如列表或字典)。默认情况下,作为用户定义类实例的对象是可清除的;它们都比较不相等,它们的哈希值是它们的id()。
由于object
提供了__hash__
,__eq__
和__cmp__
的默认实现,这意味着从object
派生的任何内容都是可清除的,除非明确定义是可以洗的。不允许创建可清除的可变类型,但它可能不会按您的需要运行。
答案 3 :(得分:4)
@ fred-nurk上面的例子幸运地不再适用于Python 3,因为this change:
覆盖
__eq__()
且未定义__hash__()
的类会将其__hash__()
隐式设置为None
。当类的__hash__()
方法为None
时,当程序尝试检索其哈希值时,该类的实例将引发适当的TypeError
...
感谢上帝。但是,如果你为自己明确定义__hash__()
,你仍然可以做坏事:
class BadHasher:
def __init__(self):
self.first = True
# Implement __hash__ in an evil way. The first time an instance is hashed,
# return 1. Every time after that, return 0.
def __hash__(self):
if self.first:
self.first = False
return 1
return 0
myobject = BadHasher()
# We can put this object in a set...
myset = {myobject}
# ...but as soon as we look for it, it's gone!
if myobject not in myset:
print("what the hell we JUST put it in there")