我可以在python中使用一个可变对象作为字典键。这不是不允许的吗?

时间:2010-12-11 20:20:12

标签: python dictionary

class A(object):
    x = 4

i = A()
d = {}

d[i] = 2

print d

i.x = 10

print d

我认为只有不可变对象可以是字典键,但上面的对象是可变的。

4 个答案:

答案 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")
相关问题