我正与我的一位同事争论,是否所有Python类都需要可以清理。我们这个类包含符号表达式(类似于SymPy)。
我的论点是,由于我们无法比较两个表达式的相等性,因此不应允许散列。例如,表达式'(x)'和'(1 * x)'可能比较相等,而'sqrt(x * x * x)'和'abs(x)* sqrt(x)'可能不相等。因此,'hash()'在使用符号表达式调用时应该抛出错误。
他的论点是你应该能够将所有类用作词典和集合中的键。因此,它们也必须是可以清洗的。 (我现在把话放在嘴里,他会更好地解释它。)。
谁是对的?如果你尝试哈希它们,那么它是unpythonic还是没有抛出错误的类?
答案 0 :(得分:4)
哈希函数仅在您具有明确定义的相等性测试和时才有用,因为相等性测试所考虑的信息是不可变的。
默认情况下,所有用户定义的类都按对象标识进行比较,并使用id()
作为哈希值。如果不覆盖==
运算符,则很少有理由更改此行为。如果您执行覆盖==
,并且此运算符中考虑的信息是不可变的(意味着它在实例的生命周期内无法更改),您还可以定义哈希函数使实例可以使用。
从你的问题来看,我无法确定这些情况是否成立。使类可以清除不是“Pythonic”或“Unpythonic” - 问题是如果类的语义允许散列或不散列。
答案 1 :(得分:3)
有许多不可清除的内置Python类型。所以对于一个不能清洗的课程来说,这是完美的Pythonic。
您提供的示例是创建可散列类问题的一个很好的示例,因为要使对象可用作字典中的键,它必须同时实现__hash__()
和__eq__()
。如果你不能可靠地确定平等,那么可持续性无论如何都没有真正的好处,实现它是浪费精力。
答案 2 :(得分:3)
我在这里看到的问题是你正在使用两种不同的平等概念。如果我正确理解了您的评论,那么您已覆盖__eq__
以返回表达式,将两个参数合并到==
。如果所述表达式将评估为True(在某种意义上),那么这两个表达式是相等的;如果你的表达式类也以__nonzero__
返回__bool__
iff表达式为真的方式实现__nonzero__
(True
),那么表面看来似乎就好了这应该工作正常。
但事实上,在我看来,你所定义的平等概念是一种非常不同的平等概念,而不是Python中正常的平等概念。可持续性的基本要求是,如果两个项目评估为相等,那么它们应该完全可互换。虽然你的两个表达式对象可能被评估为“相等”,但我不确定它们是否可以互换!毕竟,5 + 5
和8 + 2
会评估相同的结果,但它们不是相同的,是吗?鉴于这两个表达式,我怀疑很多人会希望它们在字典中散列到两个独立的分档中!
然而,如果不给__eq__
一个更传统的定义,那么这种行为将会很困难。正如文档所说,“Hashable objects which compare equal must have the same hash value。”因此,如果__eq__
表示5 + 5
和8 + 2
相等,那么它们必须哈希到相同的值。这意味着要使表达式像现在一样可以清除,您必须选择能够为所有评估为相等的表达式确定规范形式的__hash__
。这听起来非常难。
简而言之,如果这些表达式是不可变的,并且如果表达式相同(比“相等”更强的要求),如果重新定义__eq__
以返回True
,然后应该没有问题使他们可以清洗。另一方面,我认为不可变的不可变类型没有任何问题;如果不重新定义__eq__
,我建议您不要尝试使用表达式。
所以这一切都归结为你想以非常规方式定义__eq__
的严重程度。我想总的来说,我会采用__eq__
的传统定义,只是为了避免产生意外行为。毕竟,special cases aren't special enough to break the rules。
答案 3 :(得分:2)
拥有不可修复的课程肯定不是单声道的,虽然你的理由不是我给的通常的理由。类可能不可用的主要原因是它是可变的,因此它的核心数据本身是不可用的。例如,包裹字典或列表的类就是这种情况。
在平等比较中,我并不完全遵循你的逻辑。你说你不能比较表达式的相等性,但是你说某些表达式可能会或者可能不会相等。你能不能比较他们的平等?如果你做不到,那么说他们比较相等或不相等是没有意义的。