python - 从 dict 继承时不一致的对象比较行为

标签 python comparison-operators

这个问题是由一个失败的测试引起的,该测试拒绝在本地失败,并且只会在我们的 CI 服务器上失败。

事实证明,一些相当狡猾的对象比较是无意中进行的。

我现在很好奇为什么相同 Python 版本 (2.7.9) 的两个安装之间的行为如此不同。

这个测试用例可能会进一步简化,但这是我得到的:

import operator


class Thing(dict):
    def __int__(self, number):
        return self['number']

    def __gt__(self, other):
        return self['number'] > other

thing = Thing({'number': 2})

for o in [
        operator.lt,
        operator.le,
        operator.eq,
        operator.ne,
        operator.ge,
        operator.gt]:
    print o
    print o(0.01, thing)
    print o(thing, 0.01)

在本地运行的结果是:

<built-in function lt>
True
False
<built-in function le>
True
False
<built-in function eq>
False
False
<built-in function ne>
True
True
<built-in function ge>
False
True
<built-in function gt>
False
True

但在 Travis CI 服务器上它是:

<built-in function lt>
True
True
<built-in function le>
False
True
<built-in function eq>
False
False
<built-in function ne>
True
True
<built-in function ge>
True
False
<built-in function gt>
True
True

Python 退回到了什么样的比较行为,为什么它会在同一版本的两个安装上表现出如此不同的行为?

我最初的想法是某种基于 id 的比较,但是从 id 的值来看,它们与比较。

更新:

只有当类继承自 dict 时才会出现这种不同的行为。当它从 object 继承时,比较在两个安装上的行为相同,并给出与上述本地结果相同的结果。

更新 2:

我刚刚发现我可以仅使用 __int____gt__ 方法进一步简化测试用例,但是如果我删除其中任何一个方法,那么奇怪的行为消失了。

最佳答案

如评论中所述,dict已经定义了所有的比较运算符。 documented行为是:

Objects of different types, except different numeric types and different string types, never compare equal; such objects are ordered consistently but arbitrarily

换句话说,字典被专门定义为允许与其他类型进行比较,但此类比较的结果是未定义的。 (这在 Python 3 中已更改,因此不再允许此类类型间比较。)

当您只为您的类型覆盖一些比较运算符时,您会使事情变得更加复杂。由于您的类型定义了 __gt__但不是 __lt__ , thing > 0.01将使用您的自定义 __gt__ ,但是thing < 0.01将使用默认的(未定义的)比较行为。因此,您得到的类型有时会使用确定性规则,有时会给出未定义的行为,具体取决于您使用的比较运算符。我不知道为什么你会看到你所看到的结果的精确模式,但最重要的是你的类依赖于未定义的行为,所以你不能期望使用这种类型的比较有任何一致性。 Python 的两种实现可能在某些神秘的实现级别上做不同的事情,从而产生不同的未定义行为。未定义行为的要点是您不应该知道它是如何工作的(否则您可能会开始依赖它)。

顺便说一句,total_ordering这是一个空操作,如果你删除它,行为应该是一样的。 total_ordering仅添加尚未定义的比较运算符,但 dict已经定义了所有这些,所以 total_ordering不会做任何事情。如果您想在已经定义了自己的比较行为(如 dict)的类型的子类上建立自己的排序关系,那么您需要手动覆盖每个单独的比较运算符。

关于python - 从 dict 继承时不一致的对象比较行为,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/28887383/

相关文章:

javascript - else if 语句的奇怪行为(javascript)

python - 将 Matlab @ 翻译成 Python 代码(RuntimeWarning : invalid value encountered in true_divide )

python - Postgres+SQLAlchemy 在使用 default=func.now() 时将时间转换为 UTC

python - 当我的循环第二次运行时,为什么会出现错误?类型错误 : 'int' object has no attribute '__getitem__'

c++ - 删除 "using namespace std::rel_ops"可以改变行为吗?

for循环中的比较运算符(C语言)

javascript - 根据文本框中的值范围重定向到 iframe 中的 URL

python - Selenium 无法定位元素(Python)WebScraping

Python:XML 列表索引超出范围

php - 为什么在 PHP 中对日期格式为 "YYYY-MM-DD"的两个字符串进行小于或大于比较工作,即使它们是字符串?