Fal*_*rri 80 python comparison operators python-datamodel
我有一个类,我想覆盖__eq__()
运算符.似乎我应该覆盖__ne__()
运算符,但是__ne__
基于__eq__
这样实现它是否有意义?
class A:
def __eq__(self, other):
return self.value == other.value
def __ne__(self, other):
return not self.__eq__(other)
Run Code Online (Sandbox Code Playgroud)
或者,Python使用这些运算符的方式是否缺少某些东西,这不是一个好主意?
Aar*_*all 104
Python,我应该
__ne__()
基于实现运算符__eq__
吗?
==
而不是__eq__
在Python 3中,默认情况下!=
是否定==
,因此您甚至不需要编写__ne__
文档,并且文档不再以编写文档为主.
一般来说,对于仅限Python 3的代码,除非需要掩盖父实现,例如对于内置对象,否则不要编写代码.
也就是说,请记住Raymond Hettinger的评论:
仅当尚未在超类中定义时,该
__ne__
方法__eq__
才会 自动进行__ne__
.所以,如果你继承了内置函数,最好覆盖它们.
如果您需要在Python 2中使用您的代码,请遵循Python 2的建议,它将在Python 3中正常工作.
在Python 2中,Python本身并不会自动实现任何操作 - 因此,您应该__ne__
根据==
而不是使用来定义__eq__
.例如
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)`
Run Code Online (Sandbox Code Playgroud)
见证明
__ne__()
基于__eq__
和实现运算符__ne__
在Python 2中实现在下面的演示中提供了错误的行为.
Python 2 的文档说:
比较运算符之间没有隐含的关系.事实
x==y
并非暗示这x!=y
是错误的.因此,在定义时__eq__()
,还应该定义__ne__()
操作符将按预期运行.
所以这意味着如果我们__ne__
根据逆的定义来定义__eq__
,我们就可以获得一致的行为.
默认情况下,除非是结果,否则
__ne__()
委托__eq__()
并反转结果NotImplemented
.
在"什么是新的"部分,我们看到这种行为已经改变:
!=
现在返回相反的==
,除非==
返回NotImplemented
.
为了实现__ne__
,我们更喜欢使用==
运算符而不是__eq__
直接使用该方法,以便如果self.__eq__(other)
子类返回NotImplemented
已检查的类型,Python将适当地检查other.__eq__(self)
来自文档:
NotImplemented
对象此类型具有单个值.有一个具有此值的对象.可以通过内置名称访问此对象
NotImplemented
.如果数值方法和丰富的比较方法未实现所提供操作数的操作,则它们可能会返回此值.(然后,解释器将尝试反射操作或其他一些后备操作,具体取决于操作员.)其真值是真的.
当给定一个丰富比较运算符,如果他们不相同的类型,Python的检查,如果other
是一个子类型,并且如果它具有定义的操作者,它使用other
第一的方法(逆为<
,<=
,>=
和>
).如果NotImplemented
返回,则使用相反的方法.(它不是检查相同的方法两次.)使用==
操作员允许这种逻辑发生.
在语义上,您应该__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
Run Code Online (Sandbox Code Playgroud)
也就是说,上述两个函数应始终返回相同的结果.但这取决于程序员.
__ne__
基于以下定义来演示意外行为__eq__
:首先是设置:
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."""
Run Code Online (Sandbox Code Playgroud)
实例化非等效实例:
right1, right2 = ComparableRight(1), ChildComparableRight(2)
wrong1, wrong2 = ComparableWrong(1), ChildComparableWrong(2)
right_py3_1, right_py3_2 = BaseEquatable(1), ChildComparablePy3(2)
Run Code Online (Sandbox Code Playgroud)
(注意:虽然以下每一个的每一个断言都是等价的,因此在逻辑上多余于它之前的断言,我将它们包括在内,以证明当一个是另一个的子类时,顺序无关紧要.)
这些实例已__ne__
实现==
:
assert not right1 == right2
assert not right2 == right1
assert right1 != right2
assert right2 != right1
Run Code Online (Sandbox Code Playgroud)
这些在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
Run Code Online (Sandbox Code Playgroud)
并回想一下这些已__ne__
实现__eq__
- 虽然这是预期的行为,但实现是不正确的:
assert not wrong1 == wrong2 # These are contradicted by the
assert not wrong2 == wrong1 # below unexpected behavior!
Run Code Online (Sandbox Code Playgroud)
请注意,这种比较与上面的比较(not wrong1 == wrong2
)相矛盾.
>>> assert wrong1 != wrong2
Traceback (most recent call last):
File "<stdin>", line 1, in <module>
AssertionError
Run Code Online (Sandbox Code Playgroud)
和,
>>> assert wrong2 != wrong1
Traceback (most recent call last):
File "<stdin>", line 1, in <module>
AssertionError
Run Code Online (Sandbox Code Playgroud)
__ne__
在Python 2中跳过有关您不应该__ne__
在Python 2中跳过实现的证据,请参阅以下等效对象:
>>> right_py3_1, right_py3_1child = BaseEquatable(1), ChildComparablePy3(1)
>>> right_py3_1 != right_py3_1child # as evaluated in Python 2!
True
Run Code Online (Sandbox Code Playgroud)
以上结果应该是False
!
默认的CPython的实施__ne__
是typeobject.c
在object_richcompare
:
case Py_NE:
/* By default, __ne__() delegates to __eq__() and inverts the result,
unless the latter returns NotImplemented. */
if (self->ob_type->tp_richcompare == NULL) {
res = Py_NotImplemented;
Py_INCREF(res);
break;
}
res = (*self->ob_type->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);
}
}
Run Code Online (Sandbox Code Playgroud)
我们在这里看到
__ne__
使用__eq__
?Python 3 __ne__
在C级别的默认实现细节使用__eq__
是因为较高级别==
(PyObject_RichCompare)效率较低 - 因此它也必须处理NotImplemented
.
如果__eq__
正确实现,那么否定==
也是正确的 - 它允许我们避免在我们的低级实现细节__ne__
.
使用==
使我们能够保持我们的逻辑低电平一个地方,避免处理NotImplemented
中__ne__
.
人们可能错误地认为==
可能会返回NotImplemented
.
它实际上使用与默认实现相同的逻辑__eq__
,它检查身份(请参阅下面的do_richcompare和我们的证据)
class Foo:
def __ne__(self, other):
return NotImplemented
__eq__ = __ne__
f = Foo()
f2 = Foo()
Run Code Online (Sandbox Code Playgroud)
比较:
>>> f == f
True
>>> f != f
False
>>> f2 == f
False
>>> f2 != f
True
Run Code Online (Sandbox Code Playgroud)
不要相信我的话,让我们看看性能更高:
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
Run Code Online (Sandbox Code Playgroud)
我认为这些表现数字不言自明:
>>> 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
Run Code Online (Sandbox Code Playgroud)
当您考虑low_level_python
在Python中执行逻辑时,这是有意义的,否则将在C级别上处理.
对于Python 2兼容代码,请使用not self == other
实现__ne__
.它更多:
仅在Python 3中,使用C级别的低级否定 - 它更加简单和高效(尽管程序员负责确定它是正确的).
千万不能写在高层次的Python低电平逻辑.
Dan*_*olo 49
是的,那很好.事实上,文档敦促您定义__ne__
何时定义__eq__
:
比较运算符之间没有隐含的关系.事实
x==y
并非暗示这x!=y
是错误的.因此,在定义时__eq__()
,还应该定义__ne__()
操作符将按预期运行.
在很多情况下(比如这个),它会像否定结果一样简单__eq__
,但并非总是如此.
仅作记录,一个规范正确且可交叉的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
Run Code Online (Sandbox Code Playgroud)
这适用于__eq__
您可能定义的任何对象:
not (self == other)
,不涉及一些比较烦人/复杂的情况下干扰,其中所涉及的类别之一,并不意味着结果__ne__
是一样的结果not
上__eq__
(如SQLAlchemy的的ORM,其中两个__eq__
与__ne__
返回特殊的代理对象,没有True
或False
,并尝试not
的结果__eq__
将返回False
,而不是正确的代理对象)。not self.__eq__(other)
,这个正确委托给__ne__
其他实例的时候self.__eq__
回报NotImplemented
(not self.__eq__(other)
将额外错误的,因为NotImplemented
是truthy,所以当__eq__
不知道如何进行比较,__ne__
将返回False
,这意味着这两个对象是相等的,而实际上只询问的对象不知道,这意味着默认值不相等)如果您__eq__
不使用NotImplemented
退货,则可以正常工作(开销没有意义),如果NotImplemented
有时使用退货,则可以正确处理它。而且,Python版本检查意味着如果该类import
在Python 3中为-ed ,则将__ne__
保持未定义状态,从而可以接替Python的本机高效后备__ne__
实现(上述版本的C版本)。
为什么要这样做而不是其他解决方案的解释有些奥秘。Python对于重载运算符有一些通用规则,尤其是比较运算符:
LHS OP RHS
,请尝试LHS.__op__(RHS)
,如果返回NotImplemented
,请尝试RHS.__rop__(LHS)
。例外:如果RHS
是的类的子LHS
类,则RHS.__rop__(LHS)
先进行测试。在比较操作符的情况下,__eq__
和__ne__
是自己的“ROP” S(所以测试顺序__ne__
是LHS.__ne__(RHS)
,那么RHS.__ne__(LHS)
,逆转如果RHS
是的一个子类LHS
的类)LHS.__eq__(RHS)
返回True
也并不意味着LHS.__ne__(RHS)
返回False
(实际上,甚至不需要运算符返回布尔值; SQLAlchemy之类的ORM故意不这样做,从而允许更具表达性的查询语法)。从Python 3开始,默认__ne__
实现的行为方式是这样的,但是它不是契约性的。您可以__ne__
采用与严格相反的方式进行覆盖__eq__
。因此,当您使运算符重载时,您有两个工作:
NotImplemented
,以便Python可以委派给另一个操作数的实现。not self.__eq__(other)
def __ne__(self, other):
return not self.__eq__(other)
Run Code Online (Sandbox Code Playgroud)
从不委托给另一方(如果__eq__
正确返回,则是不正确的NotImplemented
)。当self.__eq__(other)
收益NotImplemented
(这是“truthy”),你不返回False
,这样A() != something_A_knows_nothing_about
的回报False
,当它应该检查是否something_A_knows_nothing_about
知道如何比较的情况下A
,如果没有,就应该已经返回True
(如果双方都不知道如何自相比之下,它们被认为是不相等的)。如果A.__eq__
未正确实现(返回False
而不是NotImplemented
在其无法识别另一侧时),则从A
的角度来看,这是“正确的” ,返回True
(因为A
认为不相等,所以不相等),但是可能错误的something_A_knows_nothing_about
的观点,因为它从未问过something_A_knows_nothing_about
;A() != something_A_knows_nothing_about
结束了True
,但something_A_knows_nothing_about != A()
可能False
返回,或其他任何返回值。
not self == other
def __ne__(self, other):
return not self == other
Run Code Online (Sandbox Code Playgroud)
更微妙。对于99%的类,这将是正确的,包括所有__ne__
与逻辑倒数的类__eq__
。但是not self == other
违反了上述两个规则,这意味着对于__ne__
不是逻辑逆的类__eq__
,结果再次是非自反的,因为从未询问过其中一个操作数是否可以实现__ne__
,即使另一个操作数不能。最简单的示例是weirdo类,该类False
针对所有比较返回,因此A() == Incomparable()
,A() != Incomparable()
两者都返回False
。使用正确的实现A.__ne__
(一个NotImplemented
不知道如何进行比较时返回的关系),该关系是自反的。A() != Incomparable()
和Incomparable() != A()
同意结果(因为在前一种情况下,A.__ne__
return NotImplemented
,然后Incomparable.__ne__
return False
,而在后一种情况下,直接Incomparable.__ne__
返回False
)。但是,当A.__ne__
实现为时return not self == other
,A() != Incomparable()
返回True
(因为A.__eq__
返回,而不是NotImplemented
,然后Incomparable.__eq__
返回False
,并将其A.__ne__
反转为True
),而Incomparable() != A()
返回False.
您可以在这里看到一个实际的例子。
显然,一类总是返回False
两个__eq__
和__ne__
是有点怪。但正如前面所提到的,__eq__
并且__ne__
甚至不需要返回True
/ False
; SQLAlchemy ORM具有带有比较器的类,这些类返回一个用于构建查询的特殊代理对象,而不是True
/ 根本不返回False
(如果在布尔上下文中进行评估,它们是“真实的”,但永远不要在这样的上下文中对其进行评估)。
如果无法__ne__
正确重载,您将破坏该类,如代码所示:
results = session.query(MyTable).filter(MyTable.fieldname != MyClassWithBadNE())
Run Code Online (Sandbox Code Playgroud)
将起作用(假设SQLAlchemy完全知道如何插入MyClassWithBadNE
SQL字符串;这可以使用类型适配器来完成,而MyClassWithBadNE
无需完全配合),将预期的代理对象传递给filter
,而:
results = session.query(MyTable).filter(MyClassWithBadNE() != MyTable.fieldname)
Run Code Online (Sandbox Code Playgroud)
最终将传递filter
一个纯文本False
,因为它self == other
返回一个代理对象,并且not self == other
将真实的代理对象转换为False
。希望filter
在处理类似的无效参数时引发异常False
。虽然我敢肯定,很多人会认为MyTable.fieldname
应该始终在比较的左手边,但事实是,在一般情况下没有程序上的理由来强制执行此操作,正确的泛型__ne__
将以两种方式return not self == other
起作用,而仅能起作用在一种安排中。
__ne__
执行@ShadowRanger 对特殊方法的实现__ne__
是正确的:
def __ne__(self, other):
result = self.__eq__(other)
if result is not NotImplemented:
return not result
return NotImplemented
Run Code Online (Sandbox Code Playgroud)
它也恰好是__ne__
自 Python 3.4 以来特殊方法的默认实现,如Python 文档中所述:
默认情况下,
__ne__()
将__eq__()
结果委托给并反转结果,除非它是NotImplemented
。
另请注意,返回NotImplemented
不受支持的操作数的值并非特定于特殊方法__ne__
。实际上,所有特殊比较方法1和特殊数值方法2都应返回NotImplemented
不受支持的操作数的值,如Python 文档中所述:
未实现
这种类型只有一个值。有一个具有此值的对象。这个对象是通过内置的 name 访问的
NotImplemented
。如果数值方法和富比较方法没有实现对提供的操作数的操作,它们应该返回这个值。(然后解释器将尝试反射操作,或其他一些回退,这取决于操作符。)它的真值是真的。
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
Run Code Online (Sandbox Code Playgroud)
1种特殊的比较方法:__lt__
,__le__
,__eq__
,__ne__
,__gt__
和__ge__
。
2特殊的数字方法:__add__
, __sub__
, __mul__
, __matmul__
, __truediv__
, __floordiv__
, __mod__
, __divmod__
, __pow__
, __lshift__
, __rshift__
, __and__
, __xor__
,__or__
以及它们的__r*__
反射和__i*__
就地对应物。
__ne__
实现 #1@Falmarri 对特殊方法的实现__ne__
不正确:
def __ne__(self, other):
return not self.__eq__(other)
Run Code Online (Sandbox Code Playgroud)
这个实现的问题在于它不会回退到__ne__
另一个操作数的特殊方法,因为它从不返回值NotImplemented
(表达式not self.__eq__(other)
计算为值True
or False
,包括当它的子表达式self.__eq__(other)
计算为值时,NotImplemented
因为表达式bool(NotImplemented)
计算为值True
)。值的布尔计算NotImplemented
打破了比较运算符和之间的补码关系:!=
==
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
Run Code Online (Sandbox Code Playgroud)
__ne__
实现 #2@AaronHall 对特殊方法的实现__ne__
也是不正确的:
def __ne__(self, other):
return not self == other
Run Code Online (Sandbox Code Playgroud)
这种实现的问题在于它直接回退到__eq__
另一个操作数的特殊方法,绕过另一个操作数的特殊方法__ne__
,因为它从不返回值NotImplemented
(表达式not self == other
回退到__eq__
另一个操作数的特殊方法并计算为值True
或False
)。绕过方法是不正确的,因为该方法可能会产生副作用,例如更新对象的状态:
class Correct:
def __init__(self):
self.counter = 0
def __ne__(self, other):
self.counter += 1
result = self.__eq__(other)
if result is not NotImplemented:
return not result
return NotImplemented
class Incorrect:
def __init__(self):
self.counter = 0
def __ne__(self, other):
self.counter += 1
return not self == other
x, y = Correct(), Correct()
assert x != y
assert x.counter == y.counter
x, y = Incorrect(), Incorrect()
assert x != y
assert x.counter == y.counter # AssertionError
Run Code Online (Sandbox Code Playgroud)
在数学中,二元关系 ř在一组X是一组有序对(的X, ÿ中) X 2。声明(X, ÿ中) - [R读作“ X是- [R -相关于ý ”,并且由表示XRY。
集合X上的二元关系R 的性质:
对集合X上的两个二元关系R和S 的运算:
始终有效的比较关系之间的关系:
仅对连接订单有效的比较关系之间的关系:
所以,正确的Python实现比较操作符==
,!=
,<
,>
,<=
,以及>=
对应于比较关系=,?,<,>,β,γ,上述所有的数学性质和关系应持有。
比较操作x operator y
调用__operator__
其操作数之一的类的特殊比较方法:
class X:
def __operator__(self, other):
# implementation
Run Code Online (Sandbox Code Playgroud)
由于R是自反隐含xRx,因此自反比较操作x operator y
(x == y
,x <= y
和x >= y
)或自反特殊比较方法调用x.__operator__(y)
(x.__eq__(y)
,x.__le__(y)
和x.__ge__(y)
)应计算为值,True
如果x
和y
相同,即如果表达式x is y
计算为True
。由于R是非自反意味着不是xRx,因此非自反比较操作x operator y
( x != y
, x < y
and x > y
) 或非自反特殊比较方法调用x.__operator__(y)
( x.__ne__(y)
, x.__lt__(y)
and if andx.__gt__(y)
) 应评估为值False
x
y
相同,即如果表达式的x is y
计算结果为True
。自反属性被Python用于比较操作者考虑==
和相关联的特殊的比较方法__eq__
,但令人惊讶地不被认为是用于比较操作符<=
和>=
和相关联的特殊比较方法__le__
和__ge__
和漫反射的属性被Python用于比较操作者考虑!=
和相关联的特殊的比较方法__ne__
,但令人惊讶的不是考虑的比较操作符<
和>
和相关的特殊比较方法__lt__
和__gt__
. 被忽略的比较运算符会引发异常TypeError
(以及相关的特殊比较方法返回值NotImplemented
),如Python 文档中所述:
相等比较(
==
和!=
)的默认行为基于对象的身份。因此,相同身份的实例的相等比较导致相等,不同身份的实例的相等比较导致不平等。这种默认行为的动机是希望所有对象都应该是自反的(即x is y
隐含x == y
)。默认顺序的比较(
<
,>
,<=
,和>=
)不设置; 一次尝试引发TypeError
。这种默认行为的动机是缺乏与平等类似的不变量。[这是不正确的,因为<=
and>=
像 一样自反==
,并且<
和>
像 一样不自反!=
。]
该类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
callsx.__lt__(y)
、x<=y
callsx.__le__(y)
、x==y
callsx.__eq__(y)
、x!=y
callsx.__ne__(y)
、x>y
callsx.__gt__(y)
、x>=y
callsx.__ge__(y)
。如果富比较方法
NotImplemented
没有为给定的参数对实现操作,它可能会返回单例。[…]
这些方法没有交换参数版本(当左参数不支持操作但右参数支持时使用);相反,
__lt__()
和__gt__()
是彼此的反映,__le__()
并且__ge__()
是彼此的反映,__eq__()
并且__ne__()
是他们自己的反映。如果操作数的类型不同,且右操作数的类型是左操作数类型的直接或间接子类,则右操作数的反射方法优先,否则左操作数的方法优先。不考虑虚拟子类化。
由于R = ( R T ) T,比较xRy等效于反向比较yR T x(在 Python 文档中非正式地命名为“reflected”)。因此,有两种方法可以计算比较操作的结果x operator y
:调用x.__operator__(y)
or 或y.__operatorT__(x)
。Python 使用以下计算策略:
x.__operator__(y)
除非右操作数的类是左操作数的类的后代,在这种情况下它调用y.__operatorT__(x)
(允许类覆盖其祖先的 converse 特殊比较方法)。x
和y
不受支持(由返回值指示NotImplemented
),它将调用逆特殊比较方法作为第一个回退。x
和y
不受支持(由返回值指示NotImplemented
),它会引发异常,TypeError
但比较运算符除外,==
并且!=
分别比较操作数的身份和非身份,x
并y
作为第二个回退(利用==
和) 的非自反性!=
。在 CPython 中,这是用 C 代码实现的,它可以翻译成 Python 代码(名称eq
为 for ==
、ne
for !=
、lt
for <
、gt
for >
、le
for<=
和ge
for >=
):
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
Run Code Online (Sandbox Code Playgroud)
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
Run Code Online (Sandbox Code Playgroud)
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
Run Code Online (Sandbox Code Playgroud)
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
Run Code Online (Sandbox Code Playgroud)
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
Run Code Online (Sandbox Code Playgroud)
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
Run Code Online (Sandbox Code Playgroud)
由于R = ¬(¬ R ),比较xRy等效于补比较 ¬( x ¬ Ry )。? 是=的补码,所以特殊方法默认按照支持操作数__ne__
的特殊方法__eq__
实现,而其他特殊比较方法默认独立实现(事实上?是<和=的并集,而?是的并集>和=被令人惊讶地不被认为是,这意味着当前的特殊方法__le__
和__ge__
应用户实现的),如在解释Python文档:
默认情况下,
__ne__()
将__eq__()
结果委托给并反转结果,除非它是NotImplemented
。比较运算符之间没有其他隐含的关系,例如,的真值(x<y or x==y)
并不意味着x<=y
。
在 CPython 中,这是用 C 代码实现的,它可以被翻译成 Python 代码:
def __eq__(self, other):
return self is other or NotImplemented
Run Code Online (Sandbox Code Playgroud)
def __ne__(self, other):
result = self.__eq__(other)
if result is not NotImplemented:
return not result
return NotImplemented
Run Code Online (Sandbox Code Playgroud)
def __lt__(self, other):
return NotImplemented
Run Code Online (Sandbox Code Playgroud)
def __gt__(self, other):
return NotImplemented
Run Code Online (Sandbox Code Playgroud)
def __le__(self, other):
return NotImplemented
Run Code Online (Sandbox Code Playgroud)
def __ge__(self, other):
return NotImplemented
Run Code Online (Sandbox Code Playgroud)
所以默认情况下:
x operator y
引发异常TypeError
除了比较运算符==
和!=
对于其分别返回值True
和False
如果所述操作数x
和y
分别是相同的,并且不相同的,并且这些值False
和True
以其他方式;x.__operator__(y)
返回值NotImplemented
除特殊比较方法__eq__
和__ne__
用于其分别返回值True
和False
如果所述操作数x
和y
分别是相同的,并且不相同的,并且该值NotImplemented
,否则。