The __ne__ method follows automatically from __eq__ only if
__ne__ isn't already defined in a superclass. So, if you're
inheriting from a builtin, it's best to override both.
在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)`
There are no implied relationships among the comparison operators. The
truth of x==y does not imply that x!=y is false. Accordingly, when
defining __eq__(), one should also define __ne__() so that the
operators will behave as expected.
!= now returns the opposite of ==, unless == returns NotImplemented.
对于实现__ne__,我们更喜欢使用==运算符而不是直接使用__eq__方法,这样,如果子类的self.__eq__(other)返回NotImplemented对于所检查的类型,Python将适当地检查other.__eq__(self)From the documentation:
NotImplemented对象
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 may 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.
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
也就是说,上述两个函数都应该始终返回相同的结果。但这取决于程序员。
基于__eq__定义__ne__时意外行为的演示:
首先设置:
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."""
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);
}
}
Aaron Hall’s implementation not self == other of the __ne__ method is incorrect as it can never return NotImplemented (not NotImplemented is False) and therefore the __ne__ method that has priority can never fall back on the __ne__ method that does not have priority.
not self == other used to be the default Python 3 implementation of the __ne__ method but it was a bug and it was corrected in Python 3.4 on January 2015, as ShadowRanger noticed (see issue #21408).
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
There are no implied relationships
among the comparison operators. The
truth of x==y does not imply that x!=y
is false. Accordingly, when defining
__eq__(), one should also define __ne__() so that the operators will behave as expected.
如果您的__eq__不使用NotImplemented返回,那么这是有效的(无意义的开销),如果它确实有时使用NotImplemented,那么这将正确处理它。Python版本检查意味着,如果类在Python 3中是import定义的,__ne__将保持未定义状态,从而允许Python的本机高效回退^{} implementation (a C version of the above)接管。
(适用于所有运算符)运行LHS OP RHS时,请尝试LHS.__op__(RHS),如果返回NotImplemented,请尝试RHS.__rop__(LHS)。异常:如果RHS是LHS类的一个子类,则首先测试RHS.__rop__(LHS)。在比较运算符的情况下,__eq__和__ne__是它们自己的“rop”(因此__ne__的测试顺序是LHS.__ne__(RHS),然后是RHS.__ne__(LHS),如果RHS是LHS类的子类,则相反)
简而言之:不,用
==
代替__eq__
在Python 3中,
!=
在默认情况下是==
的否定,因此您甚至不需要编写__ne__
,文档也不再是对编写一个的固执己见。一般来说,对于Python 3-only代码,除非需要覆盖父实现(例如,对于内置对象),否则不要编写。
也就是说,记住Raymond Hettinger's comment:
如果您需要代码在Python 2中工作,请遵循python2的建议,它在Python 3中工作得很好。
在Python 2中,Python本身不会自动实现任何操作,因此,应该用
==
而不是__eq__
来定义__ne__
。 E、 G看看证据
__eq__
和__ne__
在下面的演示中提供不正确的行为。
冗长的回答
Python 2的documentation表示:
所以这意味着,如果我们用
__ne__
的倒数定义__eq__
,我们可以得到一致的行为。此部分文档已更新为Python 3:
在"what's new" section中,我们看到这种行为已经改变了:
对于实现
__ne__
,我们更喜欢使用==
运算符而不是直接使用__eq__
方法,这样,如果子类的self.__eq__(other)
返回NotImplemented
对于所检查的类型,Python将适当地检查other.__eq__(self)
From the documentation:NotImplemented
对象当给定一个丰富的比较运算符时,如果它们不是同一类型,Python将检查
other
是否是子类型,如果定义了该运算符,则首先使用other
的方法(与<
、<=
、>=
和>
相反)。如果返回NotImplemented
,则使用相反的方法。(它不会两次检查同一个方法)使用==
运算符允许发生这种逻辑。期望值
从语义上讲,您应该在检查相等性方面实现
__ne__
,因为您的类的用户将期望以下函数对于A的所有实例都是等效的:也就是说,上述两个函数都应该始终返回相同的结果。但这取决于程序员。
基于
__eq__
定义__ne__
时意外行为的演示:首先设置:
实例化非等效实例:
预期行为:
(注意:虽然下面每一个的第二个断言都是等价的,因此在逻辑上与之前的断言是多余的,但我将它们包括在内,以证明当一个是另一个的子类时,顺序并不重要。)
这些实例使用
==
实现了__ne__
:在Python 3下测试的这些实例也可以正常工作:
回想一下,它们用
__eq__
实现了__ne__
-虽然这是预期的行为,但实现是不正确的:意外行为:
注意,这个比较与上面的比较相矛盾(
not wrong1 == wrong2
)。而且
不要跳过Python 2中的
__ne__
有关不应跳过在Python 2中实现
__ne__
的证据,请参见以下等效对象:上面的结果应该是
False
!Python 3源代码
__ne__
的默认CPython实现位于^{我们看到了
但是默认的
__ne__
使用__eq__
?Python 3在C级别的默认
__ne__
实现细节使用__eq__
,因为较高级别的==
(PyObject_RichCompare)效率较低,因此它还必须处理NotImplemented
。如果
__eq__
的实现是正确的,那么==
的否定也是正确的,它允许我们在__ne__
中避免低级的实现细节。使用
==
可以让我们将低级逻辑保持在一个位置,并避免在__ne__
中寻址NotImplemented
。有人可能错误地认为
==
可能返回NotImplemented
。它实际上使用的逻辑与
__eq__
的默认实现相同,后者检查标识(请参见do_richcompare和下面的证据)以及比较:
性能
别相信我的话,让我们看看还有什么表现:
我认为这些业绩数字说明了问题:
当您认为
low_level_python
在Python中执行逻辑时,这是有意义的,否则将在C级别上进行处理。对一些批评的回应
另一位回答者写道:
让
__ne__
从不返回NotImplemented
不会使其不正确。相反,我们通过检查与==
是否相等来处理NotImplemented
的优先级排序。假设==
被正确实现,我们就完成了。好吧,我们来解释一下。
如前所述,Python 3默认处理
__ne__
,方法是首先检查self.__eq__(other)
是否返回NotImplemented
(singleton),如果返回is
则应使用is
进行检查,否则应返回相反值。下面是作为类mixin编写的逻辑:这对于C级Python API的正确性是必要的,它是在Python 3中引入的,使得
多余的。所有相关的
__ne__
方法都被删除,包括实现自己的检查的方法以及直接或通过==
委托给__eq__
的方法,而==
是最常见的方法。结论
对于与Python 2兼容的代码,使用
==
来实现__ne__
。更重要的是:仅在Python 3中,在C级别使用低级否定-它甚至更简单、更高效(尽管程序员负责确定它是正确的)。
同样,不要用高级Python编写低级逻辑。
是的,那很好。实际上,the documentation在定义
__eq__
时,会敦促您定义__ne__
:在很多情况下(比如这一个),它将像否定
__eq__
的结果一样简单,但并不总是如此。仅作为记录,一个规范正确且跨Py2/Py3的可移植
__ne__
看起来像:这适用于任何
__eq__
您可以定义:not (self == other)
不同的是,在一些烦人的/复杂的情况下,如果涉及的类之一并不意味着__ne__
的结果与not
的结果相同(例如,SQLAlchemy的ORM,其中__eq__
和__ne__
都返回特殊的代理对象,而不是True
或False
,并且尝试not
,__eq__
的结果将返回False
,而不是正确的代理对象)。not self.__eq__(other)
不同,当self.__eq__
返回NotImplemented
时,这正确地委托给另一个实例的__ne__
(not self.__eq__(other)
将是额外错误的,因为NotImplemented
是真实的,所以当__eq__
不知道如何执行比较时,__ne__
将返回False
,这意味着两个对象是相等的,而实际上只有一个对象被请求不知道,这意味着违约不平等)如果您的} implementation (a C version of the above) 接管。
__eq__
不使用NotImplemented
返回,那么这是有效的(无意义的开销),如果它确实有时使用NotImplemented
,那么这将正确处理它。Python版本检查意味着,如果类在Python 3中是import
定义的,__ne__
将保持未定义状态,从而允许Python的本机高效回退^{为什么需要这个
Python重载规则
为什么你这样做,而不是其他解决方案的解释有些晦涩难懂。Python有几个关于重载运算符的一般规则,特别是比较运算符:
LHS OP RHS
时,请尝试LHS.__op__(RHS)
,如果返回NotImplemented
,请尝试RHS.__rop__(LHS)
。异常:如果RHS
是LHS
类的一个子类,则首先测试RHS.__rop__(LHS)
。在比较运算符的情况下,__eq__
和__ne__
是它们自己的“rop”(因此__ne__
的测试顺序是LHS.__ne__(RHS)
,然后是RHS.__ne__(LHS)
,如果RHS
是LHS
类的子类,则相反)LHS.__eq__(RHS)
返回True
并不意味着LHS.__ne__(RHS)
返回False
(事实上,运算符甚至不需要返回布尔值;像SQLAlchemy这样的ORMs有意不返回,允许更具表现力的查询语法)。从Python 3开始,默认的__ne__
实现以这种方式运行,但它不是契约式的;您可以以与__eq__
完全相反的方式重写__ne__
。如何应用于重载比较器
所以当你重载一个操作符时,你有两个任务:
NotImplemented
,这样Python就可以委托给另一个操作数的实现关于
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
认为它不相等,所以它不等于l) ,但从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
的问题更微妙。99%的类都是正确的,包括}返回
__ne__
与__eq__
逻辑相反的所有类。但是not self == other
破坏了上面提到的两个规则,这意味着对于__ne__
不是__eq__
的逻辑逆的类,结果再次是非自反的,因为从来不会询问其中一个操作数是否能够实现__ne__
,即使另一个操作数不能,最简单的例子是一个奇怪的类,它返回False
进行all比较,因此A() == Incomparable()
和A() != Incomparable()
都返回False
。如果A.__ne__
(在不知道如何进行比较时返回NotImplemented
)的正确实现,则关系是自反的;A() != Incomparable()
和Incomparable() != A()
同意结果(因为在前一种情况下,A.__ne__
返回NotImplemented
,然后^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.
您可以在操作here中看到这个示例。
显然,对于
__eq__
和__ne__
总是返回False
的类有点奇怪。但如前所述,__eq__
和__ne__
甚至不需要返回True
/False
;SQLAlchemy ORM有带比较器的类,这些比较器返回用于查询生成的特殊代理对象,而不是True
/False
(如果在布尔上下文中求值,则它们是“truthy”,但不应该在这样的上下文中求值)。如果未能正确地重载
__ne__
,则将中断此类类,如下所示:将工作(假设SQLAlchemy知道如何将
MyClassWithBadNE
插入到SQL字符串中;这可以通过类型适配器来完成,而不需要MyClassWithBadNE
进行协作),将预期的代理对象传递给filter
,而:最终将传递
filter
一个普通的False
,因为self == other
返回一个代理对象,而not self == other
只是将truthy代理对象转换为False
。希望,filter
在处理无效参数(如False
)时引发异常。虽然我相信很多人会争辩说MyTable.fieldname
应该在比较的左边保持一致,但事实上,在一般情况下,没有程序化的理由来强制执行这个,正确的泛型__ne__
将以任何一种方式工作,而return not self == other
只在一种安排中工作。相关问题 更多 >
编程相关推荐