__ne__ 应该实现为__ eq__ 的否定式吗?

我有一个类,我想在其中重写 __eq__方法。我似乎也应该覆盖 __ne__方法。我应该实施 __ne__作为否定的 __eq__本身或这是一个坏主意?

class A:


def __init__(self, state):
self.state = state


def __eq__(self, other):
return self.state == other.state


def __ne__(self, other):
return not self.__eq__(other)
53911 次浏览

是的,这完全没有问题。事实上,当你定义 __eq__时,文件敦促你定义 __ne__:

没有隐含的关系 在比较运算符之间 真理的 x==y并不意味着 x!=y 是错误的。因此,当定义 __eq__(), one should also define __ne__() so that the operators will behave as expected.

In a lot of cases (such as this one), it will be as simple as negating the result of __eq__, but not always.

Python,我是否应该基于 __eq__实现 __ne__()操作符?

简短的回答: 不要实现它,但是如果必须的话,使用 ==,而不是 __eq__

在 Python 3中,!=在默认情况下是否定 ==的,因此您甚至不需要编写 __ne__,并且文档不再对编写 __ne__有自己的看法。

一般来说,对于仅用于 Python 3的代码,不要编写任何代码,除非您需要遮盖父实现,例如,对于内置对象。

也就是说,请记住 Raymond Hettinger 的评论:

只有在下列情况下,__ne__方法才自动从 __eq__跟踪 __ne__ isn't already defined in a superclass. So, if you're inheriting from a builtin, it's best to override both.

如果您需要您的代码在 Python2中工作,请遵循 Python2的建议,它将在 Python3中工作得很好。

在 Python 2中,Python 本身并不自动实现任何操作,因此,应该根据 ==而不是 __eq__来定义 __ne__。 E.G.

class A(object):
def __eq__(self, other):
return self.value == other.value


def __ne__(self, other):
return not self == other # NOT `return not self.__eq__(other)`

证明给我看

  • 基于 __eq__
  • 完全没有在 Python2中实现 __ne__

在下面的演示中提供了不正确的行为。

长答案

Python 2的 documentation表示:

比较运算符之间没有隐含的关系 x==y的真值并不意味着 x!=y为假。因此,当 定义 __eq__()时,还应该定义 __ne__(),以便 operators will behave as expected.

这意味着如果我们用 __eq__的倒数来定义 __ne__,我们可以得到一致的行为。

文档的这一部分已经针对 巨蟒3:进行了更新

By default, __ne__() delegates to __eq__() and inverts the result 除非是 NotImplemented

“最新消息”部分中,我们看到这种行为已经改变:

  • != now returns the opposite of ==, unless == returns NotImplemented.

为了实现 __ne__,我们更喜欢使用 ==操作符 ,而不是直接使用 __eq__方法,这样如果子类的 self.__eq__(other)返回 NotImplemented作为检查的类型,Python 将适当地检查 other.__eq__(self) 从文件上看来的:

NotImplemented对象

此类型只有一个值。有一个具有此值的对象。通过内置名称访问此对象 返回数值方法和富比较方法 如果它们不实现操作数的操作,则返回此值 然后,解释器将尝试反射的操作,或者 它的真值是 没错。

When given a rich comparison operator, if they're not the same type, Python checks if the other is a subtype, and if it has that operator defined, it uses the other's method first (inverse for <, <=, >= and >). If NotImplemented is returned, 那么 it uses the opposite's method. (It does 没有 check for the same method twice.) Using the == operator allows for this logic to take place.


期望

从语义上讲,您应该根据检查相等性来实现 __ne__,因为您的类的用户会期望下列函数对于 A 的所有实例都是等价的:

def negation_of_equals(inst1, inst2):
"""always should return same as not_equals(inst1, inst2)"""
return not inst1 == inst2


def not_equals(inst1, inst2):
"""always should return same as negation_of_equals(inst1, inst2)"""
return inst1 != inst2

That is, both of the above functions should always return the same result. But this is dependent on the programmer.

基于 __eq__定义 __ne__时的意外行为演示:

First the setup:

class BaseEquatable(object):
def __init__(self, x):
self.x = x
def __eq__(self, other):
return isinstance(other, BaseEquatable) and self.x == other.x


class ComparableWrong(BaseEquatable):
def __ne__(self, other):
return not self.__eq__(other)


class ComparableRight(BaseEquatable):
def __ne__(self, other):
return not self == other


class EqMixin(object):
def __eq__(self, other):
"""override Base __eq__ & bounce to other for __eq__, e.g.
if issubclass(type(self), type(other)): # True in this example
"""
return NotImplemented


class ChildComparableWrong(EqMixin, ComparableWrong):
"""__ne__ the wrong way (__eq__ directly)"""


class ChildComparableRight(EqMixin, ComparableRight):
"""__ne__ the right way (uses ==)"""


class ChildComparablePy3(EqMixin, BaseEquatable):
"""No __ne__, only right in Python 3."""

实例化不等效的实例:

right1, right2 = ComparableRight(1), ChildComparableRight(2)
wrong1, wrong2 = ComparableWrong(1), ChildComparableWrong(2)
right_py3_1, right_py3_2 = BaseEquatable(1), ChildComparablePy3(2)

预期行为:

(注意: 虽然下面每一个断言的每一秒都是等价的,因此在逻辑上是多余的,但是我包含它们是为了证明 当一个是另一个的子类时,顺序并不重要。)

这些实例使用 ==实现了 __ne__:

assert not right1 == right2
assert not right2 == right1
assert right1 != right2
assert right2 != right1

在 Python 3下进行测试的这些实例也能正常工作:

assert not right_py3_1 == right_py3_2
assert not right_py3_2 == right_py3_1
assert right_py3_1 != right_py3_2
assert right_py3_2 != right_py3_1

And recall that these have __ne__ implemented with __eq__ - while this is the expected behavior, the implementation is incorrect:

assert not wrong1 == wrong2         # These are contradicted by the
assert not wrong2 == wrong1         # below unexpected behavior!

意外行为:

请注意,这种比较与上面的比较(not wrong1 == wrong2)相矛盾。

>>> assert wrong1 != wrong2
Traceback (most recent call last):
File "<stdin>", line 1, in <module>
AssertionError

还有,

>>> assert wrong2 != wrong1
Traceback (most recent call last):
File "<stdin>", line 1, in <module>
AssertionError

在 Python 2中不要跳过 __ne__

有关不应跳过在 Python2中实现 __ne__的证据,请参阅以下等价对象:

>>> right_py3_1, right_py3_1child = BaseEquatable(1), ChildComparablePy3(1)
>>> right_py3_1 != right_py3_1child # as evaluated in Python 2!
True

上面的结果应该是 False

Python 3源代码

__ne__的默认 CPython 实现在 ABC1在 object_richcompare中:

case Py_NE:
/* By default, __ne__() delegates to __eq__() and inverts the result,
unless the latter returns NotImplemented. */
if (Py_TYPE(self)->tp_richcompare == NULL) {
res = Py_NotImplemented;
Py_INCREF(res);
break;
}
res = (*Py_TYPE(self)->tp_richcompare)(self, other, Py_EQ);
if (res != NULL && res != Py_NotImplemented) {
int ok = PyObject_IsTrue(res);
Py_DECREF(res);
if (ok < 0)
res = NULL;
else {
if (ok)
res = Py_False;
else
res = Py_True;
Py_INCREF(res);
}
}
break;

但是默认的 __ne__使用 __eq__

Python 3在 C 级的默认 __ne__实现细节使用 __eq__,因为高级的 ==(PyObject_RichCompare)效率较低,因此它也必须处理 NotImplemented

If __eq__ is correctly implemented, then the negation of == is also correct - and it allows us to avoid low level implementation details in our __ne__.

使用 ==允许我们将低级逻辑保持在 位置,而 避免寻址在 __ne__中保持在 NotImplemented位置。

One might incorrectly assume that == may return NotImplemented.

它实际上使用了与 __eq__默认实现相同的逻辑,__eq__检查身份(参见下面的 做比较和我们的证据)

class Foo:
def __ne__(self, other):
return NotImplemented
__eq__ = __ne__


f = Foo()
f2 = Foo()

还有比较:

>>> f == f
True
>>> f != f
False
>>> f2 == f
False
>>> f2 != f
True

表演

不要相信我的话,让我们看看什么更有表现力:

class CLevel:
"Use default logic programmed in C"


class HighLevelPython:
def __ne__(self, other):
return not self == other


class LowLevelPython:
def __ne__(self, other):
equal = self.__eq__(other)
if equal is NotImplemented:
return NotImplemented
return not equal


def c_level():
cl = CLevel()
return lambda: cl != cl


def high_level_python():
hlp = HighLevelPython()
return lambda: hlp != hlp


def low_level_python():
llp = LowLevelPython()
return lambda: llp != llp

我认为这些业绩数据说明了一切:

>>> import timeit
>>> min(timeit.repeat(c_level()))
0.09377292497083545
>>> min(timeit.repeat(high_level_python()))
0.2654011140111834
>>> min(timeit.repeat(low_level_python()))
0.3378178110579029

当考虑到 low_level_python在 Python 中执行逻辑时,这是有意义的,否则将在 C 级别上处理。

回应一些批评

另一位回答者写道:

Aaron Hall 对 __ne__方法的 not self == other实现是不正确的,因为它永远不能返回 NotImplemented(not NotImplementedFalse) ,因此具有优先级的 __ne__方法永远不能回到没有优先级的 __ne__方法上。

__ne__永远不返回 NotImplemented并不意味着它是不正确的。相反,我们通过检查与 ==的相等性来处理与 NotImplemented的优先级排序。假设 ==正确实现,我们就完成了。

not self == other曾经是 __ne__方法的缺省 Python 3实现,但它是一个 bug,正如 ShadowRanger 所注意到的(参见问题 # 21408) ,它在2015年1月的 Python 3.4中得到了纠正。

我们来解释一下。

如前所述,Python 3默认情况下处理 __ne__的方式是首先检查 self.__eq__(other)是否返回 NotImplemented(一个单例)——应该用 is检查 NotImplemented,如果返回,则返回 NotImplemented,否则它应该返回相反的值。下面是作为类混合的逻辑:

class CStyle__ne__:
"""Mixin that provides __ne__ functionality equivalent to
the builtin functionality
"""
def __ne__(self, other):
equal = self.__eq__(other)
if equal is NotImplemented:
return NotImplemented
return not equal

This is necessary for correctness for C level Python API, and it was introduced in Python 3, making

多余的。所有相关的 __ne__方法都被删除,包括实现自身检查的方法以及直接或通过 ==委托给 __eq__的方法,而 ==是最常用的方法。

对称重要吗?

我们持续不断的批评者提供了一个病理学的例子来说明在 __ne__中处理 NotImplemented的情况,重视对称性高于一切。让我们用一个清晰的例子来说明这个论点:

class B:
"""
this class has no __eq__ implementation, but asserts
any instance is not equal to any other object
"""
def __ne__(self, other):
return True


class A:
"This class asserts instances are equivalent to all other objects"
def __eq__(self, other):
return True


>>> A() == B(), B() == A(), A() != B(), B() != A()
(True, True, False, True)

因此,按照这种逻辑,为了保持对称性,我们需要编写复杂的 __ne__,而不管 Python 版本如何。

class B:
def __ne__(self, other):
return True


class A:
def __eq__(self, other):
return True
def __ne__(self, other):
result = other.__eq__(self)
if result is NotImplemented:
return NotImplemented
return not result


>>> A() == B(), B() == A(), A() != B(), B() != A()
(True, True, True, True)

Apparently we should give no mind that these instances are both equal and not equal.

我认为对称性没有合理代码的假设和遵循文档的建议那么重要。

However, if A had a sensible implementation of __eq__, then we could still follow my direction here and we would still have symmetry:

class B:
def __ne__(self, other):
return True


class A:
def __eq__(self, other):
return False         # <- this boolean changed...


>>> A() == B(), B() == A(), A() != B(), B() != A()
(False, False, True, True)

结论

对于与 Python 2兼容的代码,使用 ==实现 __ne__:

  • 正确
  • simple
  • performant

仅在 Python 3中,使用 C 级的低级否定-它甚至是简单且高效的 更多(尽管程序员负责确定它是 正确)。

Again, do 没有 write low-level logic in high level Python.

顺便说一句,一个规范正确且交叉的 Py2/Py3便携式 __ne__看起来像:

import sys


class ...:
...
def __eq__(self, other):
...


if sys.version_info[0] == 2:
def __ne__(self, other):
equal = self.__eq__(other)
return equal if equal is NotImplemented else not equal

这适用于您可能定义的任何 __eq__:

  • not (self == other)不同的是,在一些恼人/复杂的情况下,如果所涉及的类之一并不意味着 __ne__的结果与 __eq__上的 not的结果相同(例如 SQLAlchemy 的 ORM,其中 __eq____ne__都返回特殊的代理对象,而不是 TrueFalse,并试图返回 not,那么 __eq__的结果将返回 False,而不是正确的代理对象) ,则不会受到干扰。
  • not self.__eq__(other)不同,当 self.__eq__返回 NotImplemented时,它会正确地委托给另一个实例的 __ne__(not self.__eq__(other)是额外的错误,因为 NotImplemented是真实的,所以当 __eq__不知道如何执行比较时,__ne__会返回 False,这意味着两个对象是相等的,而实际上被请求的唯一对象不知道,这意味着默认的不相等)

如果您的 __eq__不使用 NotImplemented返回,那么这种方法可以工作(带有无意义的开销) ,如果它有时使用 NotImplemented,那么这种方法可以正确地处理它。Python 版本检查意味着,如果该类在 Python 3中是 import-ed 的,那么 __ne__将保持未定义状态,从而允许 Python 的本机高效后备 __ne__实现(上面的 C 版本)接管。


为什么需要这个

Python 重载规则

为什么你这样做而不是其他解决方案的解释有点神秘。Python 有一些关于重载运算符,特别是比较运算符的一般规则:

  1. (适用于所有运算符)运行 LHS OP RHS时,请尝试 LHS.__op__(RHS),如果返回 NotImplemented,请尝试 RHS.__rop__(LHS)。例外: 如果 RHSLHS类的一个子类,那么测试 RHS.__rop__(LHS) LHS.__op__(RHS)4。在比较运算符的情况下,__eq____ne__是它们自己的“ rop”(因此 __ne__的测试顺序是 LHS.__op__(RHS)0,然后是 LHS.__op__(RHS)1,如果 RHSLHS类的子类,则颠倒)
  2. 除了“交换”操作符的概念之外,操作符之间没有隐含的关系。即使对于同一个类,返回 TrueLHS.__eq__(RHS)并不意味着返回 LHS.__ne__(RHS)(事实上,操作符甚至不需要返回布尔值; 像 SQLAlchemy 这样的 ORM 有意不这样做,允许更具表达性的查询语法)。在 Python 3中,默认的 __ne__实现是这样的,但是它不是契约式的; 您可以以与 __eq__不完全相反的方式重写 __ne__

这如何适用于重载比较器

所以当你重载一个运算符时,你有两个作业:

  1. 如果你知道如何自己实现这个操作,那么就用你自己的 只有知识来做这个比较(不要隐式或显式地委托操作的另一端; 这样做会带来不正确和/或无限递归的风险,这取决于你如何做)
  2. 如果您知道如何自己实现操作,那么 一直都是返回 NotImplemented,这样 Python 就可以委托给其他操作数的实现

not self.__eq__(other)的问题

def __ne__(self, other):
return not self.__eq__(other)

永远不要将代理委托给另一端(如果 __eq__正确返回 NotImplemented,则代理委托是不正确的)。当 self.__eq__(other)返回 NotImplemented(这是“真实的”) ,你默认返回 False,所以 A() != something_A_knows_nothing_about返回 False,当它应该检查 something_A_knows_nothing_about是否知道如何比较 A的实例,如果它不知道,它应该返回 True(因为如果任何一方都不知道如何比较对方,他们被认为是不相等的)。如果 NotImplemented0被错误地实现了(返回 False而不是 NotImplemented,当它不能识别另一边时) ,那么从 A的角度来看,这是“正确的”,返回 True(因为 A不认为它是相等的,所以它不是相等的) ,但是从 something_A_knows_nothing_about的角度来看,这可能是错误的,因为它甚至从来没有问过 something_A_knows_nothing_about; A() != something_A_knows_nothing_about最终得到 True,但是 self.__eq__(other)0可以返回 False,或者任何其他的返回值。

The problem with not self == other

def __ne__(self, other):
return not self == other

更微妙。对于99% 的类来说都是正确的,包括所有 __ne____eq__逻辑逆的类。但是 not self == other打破了上面提到的两个规则,这意味着对于 __ne__ __ne__2是 __eq__的逻辑逆的类,结果又一次是非对称的,因为从来不会询问其中一个操作数是否能够实现 __ne__,即使另一个操作数不能。最简单的例子是一个怪异的类,它为 __ne__3比较返回 False,所以 A() == Incomparable()A() != Incomparable()都返回 False。对于 __eq__0的正确实现(当它不知道如何进行比较时返回 __eq__1) ,关系是对称的; A() != Incomparable()__eq__3在结果上达成一致(因为在前一种情况下,__eq__0返回 __eq__1,然后 __eq__6返回 False,而在后一种情况下,__eq__6直接返回 False)。但是当 __eq__0实现为 not self == other1时,A() != Incomparable()返回 not self == other3(因为 not self == other4返回,而不是 __eq__1,那么 not self == other6返回 False,而 __eq__0返回 not self == other3) ,而 __eq__3返回 __ne__1

您可以在 给你中看到这方面的一个例子。

显然,对于 __eq____ne__总是返回 False的类有点奇怪。但是如前所述,__eq____ne__甚至不需要返回 True/False; SQLAlchemy ORM 有带比较器的类,它们返回一个特殊的代理对象用于查询构建,而根本不返回 True/False(如果在布尔上下文中求值,它们是“真实的”,但是它们从来不应该在这样的上下文中求值)。

By failing to overload __ne__ properly, you 威尔 break classes of that sort, as the code:

 results = session.query(MyTable).filter(MyTable.fieldname != MyClassWithBadNE())

将工作(假设 SQLAlchemy 知道如何将 MyClassWithBadNE插入到 SQL 字符串中; 这可以通过类型适配器完成,而不需要 MyClassWithBadNE的协作) ,将预期的代理对象传递给 filter,同时:

 results = session.query(MyTable).filter(MyClassWithBadNE() != MyTable.fieldname)

will end up passing filter a plain False, because self == other returns a proxy object, and not self == other just converts the truthy proxy object to False. Hopefully, filter throws an exception on being handled invalid arguments like False. While I'm sure many will argue that MyTable.fieldname False0 be consistently on the left hand side of the comparison, the fact remains that there is no programmatic reason to enforce this in the general case, and a correct generic __ne__ will work either way, while return not self == other only works in one arrangement.

__ne__的正确实现

@ ShadowRanger 特殊方法 __ne__的实现是正确的:

def __ne__(self, other):
result = self.__eq__(other)
if result is not NotImplemented:
return not result
return NotImplemented

它也恰好是特殊方法 __ne__ 从 Python 3.4开始的默认实现,如 Python 文档所述:

默认情况下,__ne__()委托给 __eq__()并反转结果,除非它是 NotImplemented

Also note that returning the value NotImplemented for unsupported operands is not specific to the special method __ne__. In fact, 所有特殊的比较方法 < sup > 1 和特殊的数值方法 < sup > 2 都应该为不支持的操作数返回值 NotImplemented, as specified in the Python documentation:

未实施

This type has a single value. There is a single object with this value. This object is accessed through the built-in name NotImplemented. Numeric methods and rich comparison methods should return this value if they do not implement the operation for the operands provided. (The interpreter will then try the reflected operation, or some other fallback, depending on the operator.) Its truth value is true.

An example for the special numeric methods is given in the Python 文档:

class MyIntegral(Integral):


def __add__(self, other):
if isinstance(other, MyIntegral):
return do_my_adding_stuff(self, other)
elif isinstance(other, OtherTypeIKnowAbout):
return do_my_other_adding_stuff(self, other)
else:
return NotImplemented


def __radd__(self, other):
if isinstance(other, MyIntegral):
return do_my_adding_stuff(other, self)
elif isinstance(other, OtherTypeIKnowAbout):
return do_my_other_adding_stuff(other, self)
elif isinstance(other, Integral):
return int(other) + int(self)
elif isinstance(other, Real):
return float(other) + float(self)
elif isinstance(other, Complex):
return complex(other) + complex(self)
else:
return NotImplemented

特殊比较方法: __lt____le____eq____ne____gt____ge__

__add____sub____mul____matmul____truediv____floordiv____mod____divmod____pow____lshift____sub__0、 __sub__1、 __sub__2、 __sub__3及其 __sub__4反射和 __sub__5原位对应。

__ne__ # 1的错误实现

@ Falmarri 使用的特殊方法 __ne__是不正确的:

def __ne__(self, other):
return not self.__eq__(other)

The problem with this implementation is that it does not fall back on the special method __ne__ of the other operand as it never returns the value NotImplemented (the expression not self.__eq__(other) evaluates to the value True or False, including when its subexpression self.__eq__(other) evaluates to the value NotImplemented since the expression bool(NotImplemented) evaluates to the value True). The Boolean evaluation of the value NotImplemented breaks the NotImplemented2 relationship between the comparison operators NotImplemented0 and NotImplemented1:

class Correct:


def __ne__(self, other):
result = self.__eq__(other)
if result is not NotImplemented:
return not result
return NotImplemented




class Incorrect:


def __ne__(self, other):
return not self.__eq__(other)




x, y = Correct(), Correct()
assert (x != y) is not (x == y)


x, y = Incorrect(), Incorrect()
assert (x != y) is not (x == y)  # AssertionError

__ne__ # 2的错误实现

@ AaronHall 特殊方法 __ne__的实现也是不正确的:

def __ne__(self, other):
return not self == other

这个实现的问题是,它直接回到另一个操作数的特殊方法 __eq__,绕过另一个操作数的特殊方法 __ne__,因为它从来不返回值 NotImplemented(表达式 not self == other回到另一个操作数的特殊方法 __eq__,计算结果为值 TrueFalse)。绕过一个方法是不正确的,因为该方法可能具有 副作用,比如更新对象的状态:

class Correct:


def __init__(self):
self.state = False


def __ne__(self, other):
self.state = True
result = self.__eq__(other)
if result is not NotImplemented:
return not result
return NotImplemented




class Incorrect:


def __init__(self):
self.state = False


def __ne__(self, other):
self.state = True
return not self == other




x, y = Correct(), Correct()
assert x != y
assert x.state == y.state


x, y = Incorrect(), Incorrect()
assert x != y
assert x.state == y.state  # AssertionError

了解比较操作

In mathematics, a 二元关系 R over a set X is a set of ordered pairs (X) in X2. The statement (X) in R reads ‘X is R-related to ’ and is denoted by R3.

集合 X上二元关系 R的性质:

  • R 反射性的当对于 X中的所有 XXRx
  • 当对于 X中的所有 X而不是 XRx,R 无意识的(也称为 严格)。
  • 对于 X中的所有 X,R 对称,如果 xRy,则 YRx
  • 对于 X中的所有 X,R 反对称,如果 XRyYRx,则 X =
  • 对于 X中的所有 Xz,R 传递式,如果 XRyYRz,则 XRz
  • 对于 XXRyyRx中的所有 Xy,R 环境卫生署(也称为 总数)。
  • R是自反的、对称的和传递的时候,R 等价关系
    例如,= 。不过≠只是对称的。
  • R是自反的、反对称的和传递的时候,R 订单关系
    例如,≤和≥。
  • R是无反射的、反对称的和传递的时,R 严格的秩序关系
    例如,< 和 > 。

集合 X上两个二元关系 R是的上的运算:

  • R< em > converse X上的 RT = {(X) | XRy}的二元关系。
  • R补充R = {(X) | 而不是 xRy}优于 X的二元关系。
  • RS工会RS = {(x) | XRyXSy}在 X上的二元关系。

始终有效的比较关系之间的关系:

  • 2互补关系: = 和≠是对方的互补关系;
  • 6逆关系: = 是本身的逆,≠是本身的逆,< 和 > 是彼此的逆,≤和≥是彼此的逆;
  • 2结合关系: ≤是 < 和 = 的结合,≥是 > 和 = 的结合。

仅对 康奈克斯顺序关系有效的比较关系之间的关系:

  • 4互补关系: < 和≥是对方的互补关系,> 和≤是对方的互补关系。

So to correctly implement in Python the comparison operators ==, !=, <, >, <=, and >= corresponding to the comparison relations =, ≠, <, >, ≤, and ≥, all the above mathematical properties and relationships should hold.

比较操作 x operator y调用其中一个操作数的类的特殊比较方法 __operator__:

class X:


def __operator__(self, other):
# implementation

由于 x.__operator__(y)0是 x.__operator__(y)1意味着 x.__operator__(y)2,如果 xx == y0是相同的,那么自反比较操作 x operator y(x == yx <= yx >= y)或自反特殊比较方法 x.__operator__(y)(x.__eq__(y)x.__le__(y)x.__ge__(y))应该计算到值 True,即如果表达 x == y1计算到 True。由于 x.__operator__(y)0是 x.__operator__(y)4意味着不是 x.__operator__(y)2,所以无反射比较操作 x operator y(x == y4,x == y5和 x == y6)或无反射特殊比较方法称为 x.__operator__(y)(x == y8,x == y9和 x <= y0)如果 xx == y0是相同的,即如果表达 x == y1的计算结果为 True,则应该计算到值 x <= y1。Python 考虑了比较运算符 x <= y6和相关特殊比较方法 x <= y7的自反性,而 x.__operator__(y)6考虑了比较运算符 x <= y8和 x <= y9以及相关特殊比较方法 x >= y0和 x >= y1的自反性,Python 考虑了比较运算符 x >= y2和相关特殊比较方法 x >= y3的自反性,而 x.__operator__(y)6考虑了比较运算符 x >= y4和 x >= y5以及相关特殊比较方法 x >= y6和 x >= y7的自反性。被忽略的比较运算符转而引发异常 x >= y8(以及相关的特殊比较方法转而返回值 x >= y9) ,如 x.__operator__(y)8中所解释的:

相等比较的默认行为(==!=)是基于 对象的身份。因此,相等的比较 具有相同标识的实例导致相等和相等 具有不同身份的实例的比较导致 这种默认行为的动机是 所有对象都应该是自反的(即 x is y意味着 x == y)。

没有提供默认顺序比较(<><=>=) ; 试图引发 TypeError。这种默认行为的动机 是缺少与等式相似的不变式。< em > [这是不正确的,因为 <=>===一样是自反的,而 <>!=一样是自反的。]

object提供了所有子类继承的特殊比较方法的默认实现,如 Python 文档中所解释的:

object.__lt__(self, other)
object.__le__(self, other)
object.__eq__(self, other)
object.__ne__(self, other)
object.__gt__(self, other)
object.__ge__(self, other)

这些就是所谓的“丰富的比较”方法 操作符符号和方法名之间的区别如下: x<y调用 x<=y呼叫 x.__le__(y) x==y呼叫 x.__eq__(y), x!=y调用 x.__ne__(y)x>y调用 x.__gt__(y)x>=y 呼叫 x.__ge__(y)

如果有下列情况,则丰富的比较方法可以返回单例 NotImplemented 它不实现给定参数对的操作。

[...]

There are no swapped-argument versions of these methods (to be used 当左参数不支持该操作而支持右参数时 实际上,__lt__()__gt__()是彼此的 反射,__le__()__ge__()互为对方的反射, __eq__() and __ne__() are their own reflection. If the operands 是不同类型的,并且右操作数的类型是直接或 左操作数类型的间接子类的 右操作数具有优先级,否则为左操作数的方法 有优先级。不考虑虚子类化。

由于 R = (RT) T,比较 xRy等同于 交谈比较 YRTx.__operator__(y)1(在 Python 文档中非正式地命名为“映射”)。因此,有两种方法可以计算比较操作 x operator y的结果: 调用 x.__operator__(y)y.__operatorT__(x)。Python 使用以下计算策略:

  1. 它调用 x.__operator__(y),除非右操作数的类是左操作数的类的后代,在这种情况下,它调用 y.__operatorT__(x)(允许类重写其祖先的逆向特殊比较方法)。
  2. 如果操作数 xy不受支持(由返回值 NotImplemented表示) ,则将反向特殊比较方法调用为 第一撤退
  3. 如果操作数 xy不受支持(由返回值 NotImplemented表示) ,则除了比较运算符 ==!=之外,它会引发异常 TypeError,其中它分别比较操作数 xy作为 y0的恒等式和非恒等式(利用 ==的反身性和 !=的反身性)。
  4. 它返回结果。

在 CPython ==2中,它可以被翻译成 Python 代码(名称 eq表示 ==ne表示 !=lt表示 <gt表示 >le表示 <===0表示 ==1) :

def eq(left, right):
if type(left) != type(right) and isinstance(right, type(left)):
result = right.__eq__(left)
if result is NotImplemented:
result = left.__eq__(right)
else:
result = left.__eq__(right)
if result is NotImplemented:
result = right.__eq__(left)
if result is NotImplemented:
result = left is right
return result
def ne(left, right):
if type(left) != type(right) and isinstance(right, type(left)):
result = right.__ne__(left)
if result is NotImplemented:
result = left.__ne__(right)
else:
result = left.__ne__(right)
if result is NotImplemented:
result = right.__ne__(left)
if result is NotImplemented:
result = left is not right
return result
def lt(left, right):
if type(left) != type(right) and isinstance(right, type(left)):
result = right.__gt__(left)
if result is NotImplemented:
result = left.__lt__(right)
else:
result = left.__lt__(right)
if result is NotImplemented:
result = right.__gt__(left)
if result is NotImplemented:
raise TypeError(
f"'<' not supported between instances of '{type(left).__name__}' "
f"and '{type(right).__name__}'"
)
return result
def gt(left, right):
if type(left) != type(right) and isinstance(right, type(left)):
result = right.__lt__(left)
if result is NotImplemented:
result = left.__gt__(right)
else:
result = left.__gt__(right)
if result is NotImplemented:
result = right.__lt__(left)
if result is NotImplemented:
raise TypeError(
f"'>' not supported between instances of '{type(left).__name__}' "
f"and '{type(right).__name__}'"
)
return result
def le(left, right):
if type(left) != type(right) and isinstance(right, type(left)):
result = right.__ge__(left)
if result is NotImplemented:
result = left.__le__(right)
else:
result = left.__le__(right)
if result is NotImplemented:
result = right.__ge__(left)
if result is NotImplemented:
raise TypeError(
f"'<=' not supported between instances of '{type(left).__name__}' "
f"and '{type(right).__name__}'"
)
return result
def ge(left, right):
if type(left) != type(right) and isinstance(right, type(left)):
result = right.__le__(left)
if result is NotImplemented:
result = left.__ge__(right)
else:
result = left.__ge__(right)
if result is NotImplemented:
result = right.__le__(left)
if result is NotImplemented:
raise TypeError(
f"'>=' not supported between instances of '{type(left).__name__}' "
f"and '{type(right).__name__}'"
)
return result

由于 R = (R) ,比较 XRy等价于 互补比较(X Ry)。≠是 = 的补充,因此特殊方法 __ne__默认是根据支持操作数的特殊方法 __eq__实现的,而其他特殊比较方法默认是独立实现的(事实上≤是 < and = 的并且≥是 > 和 = 的并且是 __eq__0的并且意味着目前特殊方法 __le____ge__应该是用户实现的) ,如 __eq__1中所解释的:

默认情况下,__ne__()委托给 __eq__()并反转结果 除非它是 NotImplemented。没有其他暗示 比较运算符之间的关系,例如,真值 (x<y or x==y)并不意味着 x<=y

在 CPython 这是用 C 代码实现的中,它可以被翻译成 Python 代码:

def __eq__(self, other):
return self is other or NotImplemented
def __ne__(self, other):
result = self.__eq__(other)
if result is not NotImplemented:
return not result
return NotImplemented
def __lt__(self, other):
return NotImplemented
def __gt__(self, other):
return NotImplemented
def __le__(self, other):
return NotImplemented
def __ge__(self, other):
return NotImplemented

因此,默认情况下:

  • 比较操作 x operator y引发除了比较操作符 ==!=之外的异常 TypeError,如果操作数 xy分别相同和不相同,则它分别返回值 TrueFalse,否则返回值 FalseTrue;
  • 特殊比较方法 x.__operator__(y)返回值 NotImplemented,但特殊比较方法 __eq____ne__除外,如果操作数 xy分别相同和不相同,则返回值 TrueFalse,否则返回值 NotImplemented