我有一种奇怪的情况。我有一个词典,self.containing_dict
。使用调试探针,我看到dict的内容,我可以看到self
是它的关键。但看看这个:
>>> self in self.containing_dict
False
>>> self in self.containing_dict.keys()
True
>>> self.containing_dict.has_key(self)
False
发生了什么事?
(我会注意到这是在弱片回调中执行的一段代码。)
更新:我被要求显示__hash__
self
的实施情况。这是:
def __hash__(self):
return hash(
(
tuple(sorted(tuple(self.args))),
self.star_args,
tuple(sorted(tuple(self.star_kwargs)))
)
)
args = property(lambda self: dict(self.args_refs))
star_args = property(
lambda self:
tuple((star_arg_ref() for star_arg_ref in self.star_args_refs))
)
star_kwargs = property(lambda self: dict(self.star_kwargs_refs))
答案 0 :(得分:5)
您描述的问题只能由self
实施__eq__
(或__cmp__
)而未实施附带的__hash__
引起。如果你没有实现__hash__
方法,你应该这样做 - 通常你不能使用定义__eq__
而不是__hash__
的对象作为dict键,但如果你继承了__hash__
可能会漏掉。
如果你实现了__hash__
,你必须确保它以正确的方式运行:结果不得在对象的生命周期内改变(或者至少只要对象作为一个dict使用键或设置项),它必须与__eq__
一致。对象的哈希值必须与它等于的对象相同(根据其__eq__
或__cmp__
。)对象的哈希值可能是不同于它不等于的物体,但它不一定是。这些要求也意味着你不能在对象的生命周期内改变__eq__
的结果,这就是为什么可变对象通常不能用作dict键的原因。
如果您的__hash__
和__eq__
不匹配,Python将无法在dicts和sets中找到该对象,但它仍会显示在dict.keys()
和{ {1}},这就是你在这里描述的内容。实现list(set)
方法的常用方法是返回您在__hash__
或hash()
方法中使用的任何属性的__eq__
。
答案 1 :(得分:2)
从您的__hash__
方法判断,该类存储对其参数的引用,并将其用作哈希。问题是,这些参数与构造对象的代码共享。如果他们更改了参数,则哈希值将发生变化,您将无法在其所在的任何词典中找到该对象。
参数不一定要复杂,只需要一个简单的列表即可。
In [13]: class Spam(object) :
....: def __init__(self, arg) :
....: self.arg = arg
....: def __hash__(self) :
....: return hash(tuple(self.arg,))
In [18]: l = range(5)
In [19]: spam = Spam(l)
In [20]: hash(spam)
Out[20]: -3958796579502723947
如果我更改我作为参数传递的列表,则哈希值将发生变化。
In [21]: l += [10]
In [22]: hash(spam)
Out[22]: -6439366262097674983
由于字典键是按哈希组织的,当我做x in d
时,Python所做的第一件事就是计算x的哈希值,然后在字典中查找具有该哈希值的东西。问题是,当一个对象的哈希值被放入字典后发生变化时,Python将查看新的哈希值,而不是在那里看到所需的键。使用密钥列表,强制Python通过相等性检查每个密钥,绕过哈希检查。
答案 2 :(得分:0)
最有可能的是,您为任何类self
定义了自定义哈希和比较,并且在将其添加到词典后对self
进行了变异。
如果你使用一个可变对象作为字典键,那么在你改变之后你可能无法在字典中访问它,但它仍然会出现在keys()
结果中。