【问题标题】:Hashable object in mappable class not contained in the mappable class. Can we call it bug?可映射类中的可哈希对象不包含在可映射类中。我们可以称之为错误吗?
【发布时间】:2021-04-24 20:23:36
【问题描述】:

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

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

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

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 的案例 5 和 6 应该是“==”而不是“in”语句)。我猜测根本原因与std库的底层c代码有关,但这无关紧要,除非标题问题的答案是肯定的。

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

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

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

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

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

*编辑:修复示例代码中的错误

【问题讨论】:

    标签: python hashtable


    【解决方案1】:

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

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

    在其中一条评论中也提到了这一点,这同样适用于/在 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 了解有关可变对象的更多信息。

    【讨论】:

    • 感谢您的友好回答。链接中的讨论对于良好实践非常有趣。感谢您的评论,我看到案例 5 和案例 6 错过了我认为不一致的行为,只是对其进行了编辑。我想说的是,Case 1、Case 3 和Case 5 理论上是在测试相同的。或者至少是案例 3 和案例 5。案例 3 和案例 5 的行为不同是我认为的错误。
    猜你喜欢
    • 1970-01-01
    • 2013-12-11
    • 2016-05-22
    • 1970-01-01
    • 1970-01-01
    • 2018-04-22
    • 1970-01-01
    • 2023-03-22
    • 1970-01-01
    相关资源
    最近更新 更多