代码之家  ›  专栏  ›  技术社区  ›  Chris AtLee

“k in d”怎么可能是假的,“k in d.keys()”怎么可能是真的?

  •  7
  • Chris AtLee  · 技术社区  · 14 年前

    我有一些python代码正在抛出keyerror异常。到目前为止,我还不能在操作环境之外进行复制,所以我不能在这里发布一个简化的测试用例。

    引发异常的代码正在这样的循环中迭代:

    for k in d.keys():
        if condition:
            del d[k]
    

    这个 del[k] 行引发异常。我添加了一个 try/except 它周围的条款,并且能够确定 k in d 是假的,但是 k in d.keys() 是真的。

    的关键 d 是旧样式类实例的绑定方法。

    类实现 __cmp__ __hash__ 所以这就是我关注的地方。

    4 回复  |  直到 14 年前
        1
  •  18
  •   adw    14 年前

    k in d.keys() 将迭代测试每个键的相等性,而 k in d 使用 __hash__ ,所以你的 _哈希__ 可能被破坏(即,它为比较相等的对象返回不同的哈希值)。

        2
  •  5
  •   Katriel    14 年前

    简单的例子说明什么是坏的,为了兴趣:

    >>> count = 0
    >>> class BrokenHash(object):
    ...     def __hash__(self):
    ...             global count
    ...             count += 1
    ...             return count
    ...
    ...     def __eq__(self, other):
    ...             return True
    ...
    >>> foo = BrokenHash()
    >>> bar = BrokenHash()
    >>> foo is bar
    False
    >>> foo == bar
    True
    >>> baz = {bar:1}
    >>> foo in baz
    False
    >>> foo in baz.keys()
    True
    
        3
  •  4
  •   Luper Rouch François Lagunas    14 年前

    不删除中的项目 d 在对其进行迭代时,将要删除的键存储在列表中,然后在另一个循环中删除它们:

    deleted = []
    for k in d.keys():
        if condition:
            deleted.append(k)
    for k in deleted:
        del d[k]
    
        4
  •  -1
  •   nearlymonolith    14 年前

    您所做的将在Java中引发一个并发修改异常。 d.keys() 创建一个键列表,当您调用它时,它们就存在了,但该列表现在是静态的-对 d 不会更改的存储版本 d.键() . 所以当你重复 d.键() 但是删除项目,你就有可能修改一个不再存在的键。

    你可以用 d.pop(k, None) ,它将返回映射到的值 k None 如果 K 不存在。这样可以避免 KeyError 问题。

    编辑:为了澄清,为了防止更多的虚降模式(负面反馈没有问题,只需提出建设性意见并发表评论,这样我们就可以进行一个潜在的信息性讨论-我来这里是为了学习和帮助):

    在这种特殊情况下,它不应该搞得一团糟,这是真的。 . 我只是把它作为一个潜在的问题提出来,因为如果他在程序的另一部分使用相同的编码方案, 不是 他对数据结构的处理非常谨慎/幸运,可能会出现这样的问题。他甚至不使用字典,而是一个实现某些方法的类,这样您就可以用类似的方式来处理它。