class A(object):
x = 4
i = A()开发者_JS百科
d = {}
d[i] = 2
print d
i.x = 10
print d
I thought only immutable objects can be dictionary keys, but the object i above is mutable.
Any object with a __hash__ method can be a dictionary key. For classes you write, this method defaults to returning a value based off id(self), and if equality is not determined by identity for those classes, you may be surprised by using them as keys:
>>> 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'
Changed in version 2.6: __hash__ may now be set to None to explicitly flag instances of a class as unhashable. [__hash__]
class Unhashable(object):
__hash__ = None
An object kan be a key in a dictionary if it is hashable.
Here is the definition of hashable from the documentation:
An object is hashable if it has a hash value which never changes during its lifetime (it needs a
__hash__()
method), and can be compared to other objects (it needs an__eq__()
or__cmp__()
method). Hashable objects which compare equal must have the same hash value.Hashability makes an object usable as a dictionary key and a set member, because these data structures use the hash value internally.
All of Python’s immutable built-in objects are hashable, while no mutable containers (such as lists or dictionaries) are. Objects which are instances of user-defined classes are hashable by default; they all compare unequal, and their hash value is their id().
Since object
provides a default implementation of __hash__
, __eq__
and __cmp__
this means that anything deriving from object
is hashable unless it is explicitly defined not to be hashable. It is not disallowed to create a mutable type that is hashable, but it might not behave as you want.
@fred-nurk's example above luckily no longer works in Python 3, because of this change:
A class that overrides
__eq__()
and does not define__hash__()
will have its__hash__()
implicitly set toNone
. When the__hash__()
method of a class isNone
, instances of the class will raise an appropriateTypeError
when a program attempts to retrieve their hash value...
Thank God for that. However, if you explicitly define __hash__()
for yourself, you can still do evil things:
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")
The requirement is that the hash of an object doesn't change over time, and that it keeps comparing equal (==) with its original value. Your class A meets both these requirements, so it makes a valid dictionary key. The x attribute is not considered at all in keying, only the object identity is.
精彩评论