开发者

I'm able to use a mutable object as a dictionary key in python. Is this not disallowed?

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 to None. When the __hash__() method of a class is None, instances of the class will raise an appropriate TypeError 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.

0

上一篇:

下一篇:

精彩评论

暂无评论...
验证码 换一张
取 消

最新问答

问答排行榜