【发布时间】:2017-02-12 03:57:41
【问题描述】:
class A(object):
x = 4
i = A()
d = {}
d[i] = 2
print d
i.x = 10
print d
我以为只有不可变对象可以是字典键,但上面的对象 i 是可变的。
【问题讨论】:
标签: python dictionary
class A(object):
x = 4
i = A()
d = {}
d[i] = 2
print d
i.x = 10
print d
我以为只有不可变对象可以是字典键,但上面的对象 i 是可变的。
【问题讨论】:
标签: python dictionary
任何具有__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
【讨论】:
如果对象是hashable,则它可以是字典中的键。
这是文档中对 hashable 的定义:
如果一个对象有一个在其生命周期内永远不会改变的哈希值(它需要一个
__hash__()方法),并且可以与其他对象进行比较(它需要一个__eq__()或__cmp__()方法),那么它就是可哈希的。比较相等的可散列对象必须具有相同的散列值。哈希性使对象可用作字典键和集合成员,因为这些数据结构在内部使用哈希值。
所有 Python 的不可变内置对象都是可散列的,而没有可变容器(例如列表或字典)是可散列的。默认情况下,作为用户定义类实例的对象是可散列的;它们都比较不相等,它们的hash值就是它们的id()。
由于object 提供了__hash__、__eq__ 和__cmp__ 的默认实现,这意味着从object 派生的任何东西都是可散列的,除非它被明确定义为不可散列。不允许创建可散列的可变类型,但它的行为可能不符合您的要求。
【讨论】:
幸运的是,上面@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")
【讨论】:
要求是对象的哈希值不会随时间变化,并且它会不断将相等 (==) 与其原始值进行比较。您的 A 类满足这两个要求,因此它是一个有效的字典键。键控中根本不考虑 x 属性,只考虑对象标识。
【讨论】: