可映射类中未包含在可映射类中的可散列对象我们可以称之为错误吗?

问题描述

首先,我对为公共项目和/或论坛做出贡献是一个完全的新手,我知道这个问题相对“基于意见”,但我不知道我应该在哪里发布这个问题,我觉得它可能对其他人有用。

在为 python 3.7.3 编写其他一些代码的测试时,我遇到了一种情况,即 Counter 的键既不在 Counter 中也不在键中。

以下代码重现并指向问题:

class MyHashable(object): 
    def __init__(self,label,key='something'):
        self.label = label
        self.key = key
    def __hash__(self):
        return hash((self.label,self.key))
    def __eq__(self,other):
        return (self.label,self.key) == (other.label,other.key)

TestDict = dict()

A = MyHashable('label1')
B = MyHashable('label2') 

B.key = 'something else' # Changing the hash of B

TestDict[A] = 12
TestDict[B] = 'ASDR'

A.key = 24               # Changing the hash of A 

Case1 = A in TestDict                  # False
Case2 = B in TestDict                  # True
Case3 = A in TestDict.keys()           # False
Case4 = B in TestDict.keys()           # True
Case5 = A in tuple(TestDict.keys())    # True
Case6 = B in tuple(TestDict.keys())    # True

我认为 Case3 不是 Case1 的根本原因,因为 Case3 在 python 2.7.17 中评估为 True。 (请记住,python2.7 的 Case 5 和 6 应该是“==”而不是“in”语句)。我猜测根本原因与std库的底层c代码有关,但这无关紧要,除非标题问题的答案是肯定的。

我相信该错误显然是我修改了影响哈希调用属性之后将它包含在字典中,但我觉得有必要在某处指出它。我认为这出现在某个地方绝对有用,这样遇到这种情况的人可能会相对较快地找到它。此外,我认为这是一个很好的例子,说明如何不创建可散列的类,或者至少是在 python 中对可散列类的不良使用的一个很好的例子。

是的,我知道字典中的这种情况不太可能发生,而且完全像程序员的错。但是在其他可映射对象(例如 collections.Counter)中,程序员可能只对计数内容感兴趣。 The Mapping Types documentation of python 3.9.1 表示:

映射对象将可散列值映射到任意对象

因此,程序员会将对象的类(程序员想要计数的)设为可散列的(在此示例中不超过 4 行),然后继续处理更重要的问题。最后,程序员会遇到一个看起来很奇怪的错误,即添加到计数器中的项目不在计数器中

进入正题。这种行为是错误吗?应该将其作为 bug 提交给 Python 核心开发人员还是其他地方?

*Edit: 修复了示例代码中的错误

解决方法

虽然实现和发现很好,但它既不是python的问题也不是错误,而是例外的行为。
当您更改键时,hash 的值会发生变化,这就是为什么在 dict 中找不到它并且是异常行为的原因。

看看这个 SO 答案 https://stackoverflow.com/questions/2671376/hashable-immutable#:~:text=In%20Python%20they're%20mostly,unusable%20as%20a%20dict%20key

在其中一条评论中也提到了同样适用于/在其他语言(如 Java)中找到的内容。
Java,如果您修改用作其中的键的对象,则 HashMap 会损坏: 旧钥匙和新钥匙都找不到,即使你打印地图,也能在那里看到。

A == tuple(TestDict.keys())[0] # True

正在返回 True,因为它正在执行字符串比较而不是对象值比较。

我得到了这样的东西

A : <__main__.MyHashable at 0x7fedfe499950>
tuple(TestDict.keys())[0]: <__main__.MyHashable at 0x7fedfe499950>

也看看这个线程 Create a dictionary in python which is indexed by lists 了解有关可变对象的更多信息。