问题:Python,我是否应该基于__eq__实现__ne __()运算符?
我有一个要覆盖__eq__()
运算符的类。我也应该重写__ne__()
运算符似乎很有意义,但是__ne__
基于__eq__
这样的实现是否有意义?
class A:
def __eq__(self, other):
return self.value == other.value
def __ne__(self, other):
return not self.__eq__(other)
还是Python缺少使用这些运算符的方式而导致的一个好主意?
回答 0
是的,那很好。实际上,文档敦促您在定义__ne__
时定义__eq__
:
比较运算符之间没有隐含的关系。的真相
x==y
并不意味着那x!=y
是错误的。因此,在定义时__eq__()
,还应该定义一个,__ne__()
以便操作符能够按预期运行。
在很多情况下(例如此情况),它__eq__
与否的结果一样简单,但并不总是如此。
回答 1
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)`
看到证明
__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
。如果数字方法和丰富比较方法未实现所提供操作数的操作,则可能返回此值。(然后,解释程序将根据操作员尝试执行反射操作或其他回退。)其真实值是true。
当给定一个丰富比较运算符,如果他们不相同的类型,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
也就是说,以上两个函数应始终返回相同的结果。但这取决于程序员。
演示__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."""
实例化非等效实例:
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
并回想起这些已__ne__
通过__eq__
– 实现,尽管这是预期的行为,但实现不正确:
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
不要__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
以上结果应该是False
!
Python 3源码
的默认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 (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__
?
__ne__
C 3级别使用Python 3的默认实现细节,__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()
和比较:
>>> 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)
not self == other
对__ne__
方法的实现是不正确的,因为它永远不会返回NotImplemented
(not NotImplemented
isFalse
),因此__ne__
具有优先级的方法永远不会退回到__ne__
没有优先级的方法。
有__ne__
从来没有回报NotImplemented
并不能使它不正确。相反,我们NotImplemented
通过与的相等性检查来处理优先级==
。假设==
正确实施,我们就完成了。
not self == other
曾经是该方法的默认Python 3实现,__ne__
但它是一个错误,正如ShadowRanger所注意到的,它在2015年1月的Python 3.4中已得到纠正(请参阅问题#21408)。
好吧,让我们解释一下。
如前所述,Python 3默认情况下__ne__
通过首先检查是否self.__eq__(other)
返回NotImplemented
(单例)来处理-应该使用with进行检查,is
如果返回则返回,否则应返回相反的值。这是作为类mixin编写的逻辑:
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
为了确保C级Python API的正确性,这是必需的,它是在Python 3中引入的,
多余的。所有相关的__ne__
方法被拆除,其中包括实施自己的支票,以及那些委托给那些__eq__
直接或通过==
-和==
是这样做的最常见的方式。
对称重要吗?
我们持批评态度提供了一个病态的例子,使办案NotImplemented
中__ne__
,重视高于一切的对称性。让我们用一个清晰的例子来说明这个论点:
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)
显然,我们不应该考虑这些实例是否相等。
我建议对称性不如假定合理的代码并遵循文档的建议重要。
但是,如果A明智地实现__eq__
,那么我们仍然可以按照我的指示进行操作,并且仍然具有对称性:
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__
。更重要的是:
- 正确
- 简单
- 表演者
仅在Python 3中,在C级别使用低级取反-它甚至更加简单和高效(尽管程序员负责确定它是正确的)。
再次,做不高层次的Python编写底层逻辑。
回答 2
仅作记录,一个规范正确且可交叉的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__
是一样的结果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重载规则
为什么要这样做而不是其他解决方案的解释有些不可思议。Python有一些关于重载运算符的通用规则,尤其是比较运算符:
- (适用于所有运算符)运行时
LHS OP RHS
,请尝试LHS.__op__(RHS)
,如果返回NotImplemented
,请尝试RHS.__rop__(LHS)
。exceptions:如果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)
从不委托给另一方(如果__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
更微妙。对于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())
将起作用(假设SQLAlchemy完全知道如何插入MyClassWithBadNE
SQL字符串;这可以使用类型适配器来完成,而MyClassWithBadNE
无需完全配合),将期望的代理对象传递给filter
,而:
results = session.query(MyTable).filter(MyClassWithBadNE() != MyTable.fieldname)
最终将传递filter
一个纯文本False
,因为它self == other
返回一个代理对象,并且not self == other
将真实的代理对象转换为False
。希望filter
在处理类似的无效参数时引发异常False
。尽管我敢肯定,很多人会认为MyTable.fieldname
应该在比较的左手边保持一致,但事实是,在一般情况下,没有程序上的理由来强制执行此操作,并且正确的泛型__ne__
将以两种方式return not self == other
起作用,而仅能起作用在一种安排中。
回答 3
简短答案:是(但请阅读文档以正确完成操作)
ShadowRanger的__ne__
方法实现是正确的(并且恰好是__ne__
自Python 3.4以来该方法的默认实现):
def __ne__(self, other):
result = self.__eq__(other)
if result is not NotImplemented:
return not result
return NotImplemented
为什么?因为它保留了重要的数学属性,所以算符的对称性!=
。此运算符是二进制的,因此其结果应取决于两个操作数的动态类型,而不仅仅是一个。这是通过针对允许多种调度的编程语言(例如Julia)的双调度实现的。在只允许单调度的Python中,通过在不支持其他操作数类型的实现方法中返回值,对数值方法和丰富的比较方法模拟了双调度。然后,解释器将尝试另一个操作数的反射方法。NotImplemented
亚伦·霍尔(Aaron Hall)not self == other
对__ne__
方法的实现是不正确的,因为它消除了!=
操作员的对称性。实际上,它永远不会返回NotImplemented
(not NotImplemented
is False
),因此__ne__
优先级较高的方法永远不会退回到__ne__
优先级较低的方法。not self == other
曾经是该方法的默认Python 3实现,__ne__
但正如ShadowRanger所注意到的那样,它是一个错误,已在2015年1月的Python 3.4中得到纠正(请参见问题#21408)。
比较运算符的实现
Python 3 的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.__lt__(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
未实现给定参数对的操作,则可能返回单例。这些方法没有交换参数版本(当left参数不支持该操作但right参数支持该操作时使用);相反,
__lt__()
and__gt__()
是彼此的反射,__le__()
and__ge__()
是彼此的反射,and__eq__()
and__ne__()
是自己的反射。如果操作数的类型不同,并且右操作数的类型是左操作数类型的直接或间接子类,则右操作数的反射方法具有优先级,否则左操作数的方法具有优先级。不考虑虚拟子类化。
将其转换为Python代码即可(使用operator_eq
for ==
,operator_ne
for !=
,operator_lt
for <
,operator_gt
for >
,operator_le
for <=
和operator_ge
for >=
):
def operator_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 operator_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 operator_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__}' and '{type(right).__name__}'")
return result
def operator_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__}' and '{type(right).__name__}'")
return result
def operator_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__}' and '{type(right).__name__}'")
return result
def operator_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__}' and '{type(right).__name__}'")
return result
比较方法的默认实现
该文档添加:
默认情况下,除非为,否则将结果
__ne__()
委托__eq__()
并反转NotImplemented
。比较运算符之间没有其他隐含关系,例如,的真相(x<y or x==y)
并不意味着x<=y
。
的比较方法的缺省的实现(__eq__
,__ne__
,__lt__
,__gt__
,__le__
和__ge__
)因此可以由下式给出:
def __eq__(self, other):
return 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
因此,这是该__ne__
方法的正确实现。而且它并不总是返回的逆__eq__
方法,因为当__eq__
方法返回NotImplemented
,它的倒数not NotImplemented
是False
(因为bool(NotImplemented)
是True
),而不是所期望的NotImplemented
。
错误的实现 __ne__
正如上文的亚伦·霍尔(Aaron Hall)所展示的,not self.__eq__(other)
该__ne__
方法不是默认的实现。但是也不是not self == other
。下面通过not self == other
在两种情况下将默认实现的行为与实现的行为进行比较来演示后者:
- 该
__eq__
方法返回NotImplemented
; - 该
__eq__
方法返回的值不同于NotImplemented
。
默认实现
让我们看看当该A.__ne__
方法使用默认实现并且该A.__eq__
方法返回时会发生什么NotImplemented
:
class A:
pass
class B:
def __ne__(self, other):
return "B.__ne__"
assert (A() != B()) == "B.__ne__"
!=
来电A.__ne__
。A.__ne__
来电A.__eq__
。A.__eq__
返回NotImplemented
。!=
来电B.__ne__
。B.__ne__
返回"B.__ne__"
。
这表明当A.__eq__
方法返回时NotImplemented
,该A.__ne__
方法将退回到该B.__ne__
方法上。
现在,让我们看看当该A.__ne__
方法使用默认实现并且该A.__eq__
方法返回的值不同于时会发生什么NotImplemented
:
class A:
def __eq__(self, other):
return True
class B:
def __ne__(self, other):
return "B.__ne__"
assert (A() != B()) is False
!=
来电A.__ne__
。A.__ne__
来电A.__eq__
。A.__eq__
返回True
。!=
返回not True
,即False
。
这表明在这种情况下,该A.__ne__
方法返回该方法的逆函数A.__eq__
。因此,该__ne__
方法的行为类似于文档中所宣传的那样。
A.__ne__
用上面给出的正确实现覆盖方法的默认实现会产生相同的结果。
not self == other
实施
让我们来看看重写的默认实现时,会发生什么A.__ne__
与方法not self == other
的实现和A.__eq__
方法返回NotImplemented
:
class A:
def __ne__(self, other):
return not self == other
class B:
def __ne__(self, other):
return "B.__ne__"
assert (A() != B()) is True
!=
来电A.__ne__
。A.__ne__
来电==
。==
来电A.__eq__
。A.__eq__
返回NotImplemented
。==
来电B.__eq__
。B.__eq__
返回NotImplemented
。==
返回A() is B()
,即False
。A.__ne__
返回not False
,即True
。
方法的默认实现__ne__
返回"B.__ne__"
,而不是True
。
现在让我们看看重写的默认实现时,会发生什么A.__ne__
与方法not self == other
的实现和A.__eq__
方法返回从值不同NotImplemented
:
class A:
def __eq__(self, other):
return True
def __ne__(self, other):
return not self == other
class B:
def __ne__(self, other):
return "B.__ne__"
assert (A() != B()) is False
!=
来电A.__ne__
。A.__ne__
来电==
。==
来电A.__eq__
。A.__eq__
返回True
。A.__ne__
返回not True
,即False
。
在这种情况下,__ne__
也会返回该方法的默认实现False
。
由于此实现无法__ne__
在__eq__
方法返回时复制该方法的默认实现的行为NotImplemented
,因此是不正确的。
回答 4
如果所有的__eq__
,__ne__
,__lt__
,__ge__
,__le__
,和__gt__
为Class意义,那么就实现__cmp__
代替。否则,由于Daniel DiPaolo所说的话,请按照您的方式做(在我进行测试而不是查找时;))