【问题标题】:I'm able to use a mutable object as a dictionary key in python. Is this not disallowed?我可以在 python 中使用可变对象作为字典键。这不是不允许的吗?
【发布时间】: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


    【解决方案1】:

    任何具有__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
    

    【讨论】:

      【解决方案2】:

      如果对象是hashable,则它可以是字典中的键。

      这是文档中对 hashable 的定义:

      如果一个对象有一个在其生命周期内永远不会改变的哈希值(它需要一个__hash__() 方法),并且可以与其他对象进行比较(它需要一个__eq__()__cmp__() 方法),那么它就是可哈希的。比较相等的可散列对象必须具有相同的散列值。

      哈希性使对象可用作字典键和集合成员,因为这些数据结构在内部使用哈希值。

      所有 Python 的不可变内置对象都是可散列的,而没有可变容器(例如列表或字典)是可散列的。默认情况下,作为用户定义类实例的对象是可散列的;它们都比较不相等,它们的hash值就是它们的id()。

      由于object 提供了__hash____eq____cmp__ 的默认实现,这意味着从object 派生的任何东西都是可散列的,除非它被明确定义为不可散列。不允许创建可散列的可变类型,但它的行为可能不符合您的要求。

      【讨论】:

        【解决方案3】:

        幸运的是,上面@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")
        

        【讨论】:

          【解决方案4】:

          要求是对象的哈希值不会随时间变化,并且它会不断将相等 (==) 与其原始值进行比较。您的 A 类满足这两个要求,因此它是一个有效的字典键。键控中根本不考虑 x 属性,只考虑对象标识。

          【讨论】:

            猜你喜欢
            • 2011-03-08
            • 2011-11-25
            • 1970-01-01
            • 1970-01-01
            • 2012-03-18
            • 1970-01-01
            • 1970-01-01
            • 2012-10-31
            • 1970-01-01
            相关资源
            最近更新 更多