代码之家  ›  专栏  ›  技术社区  ›  Ram Rachum

python:`key not in my_dict` but` key in my_dict.keys()。`

  •  4
  • Ram Rachum  · 技术社区  · 14 年前

    我有个奇怪的情况。我有口述, 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
    

    发生什么事?

    (我会注意到这是在weakref回调时执行的一段代码中。)

    更新: 我被要求展示 __hash__ 实施 自己 . 这里是:

    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))    
    
    3 回复  |  直到 14 年前
        1
  •  5
  •   Thomas Wouters    14 年前

    你描述的问题只能由 self 已实施 __eq__ (或) __cmp__ )不执行伴随 __hash__ . 如果您没有实现 _哈希__ 方法,您应该这样做——通常您不能使用定义 爱斯克 但不是 _哈希__ 作为dict键,但如果您继承 _哈希__ 可能会溜走。

    如果你真的实施 _哈希__ ,您必须确保它的行为是正确的:结果在对象的生命周期内(或至少在对象作为dict键或set项使用时)不得更改,并且它必须与 爱斯克 . 对象的哈希值 必须 与它所等于的对象相同(根据 爱斯克 _凸轮轴位置__ .)对象的哈希值 可以 不同于它不等于的物体,但它不一定是。这些要求也意味着你不能得到 爱斯克 在对象的生命周期中发生变化,这就是为什么可变对象通常不能用作dict键的原因。

    如果你的 _哈希__ 爱斯克 如果不匹配,python将无法在dicts和set中找到对象,但它仍将显示在 dict.keys() list(set) ,这就是你在这里描述的。通常的实现方法 _哈希__ 方法是返回 hash() 你在你的 爱斯克 _凸轮轴位置__ 方法。

        2
  •  2
  •   AFoglia    14 年前

    从你的判断 __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通过相等性检查每个键,而不进行哈希检查。

        3
  •  0
  •   Duncan    14 年前

    很可能您为任何类定义了自定义哈希和比较 self 是一个例子,你变异了 自己 在你把它加入字典之后。

    如果使用可变对象作为字典键,那么在对其进行可变之后,您可能无法在字典中访问它,但它仍将显示在 keys() 结果。