我有一个类,我想重写\uuuuueq\uuu
方法。我也应该重写\uu\ne\uu
方法,这似乎是有道理的。我应该将\uu ne\uuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuu
A类:
定义初始化(自我,状态):
self.state=状态
定义(自身、其他):
返回self.state==other.state
定义(自身、其他):
返回非自我。均衡(其他)
Python,我应该基于
\uuu eq\uu
实现\uu ne\uuu()操作符吗
简短回答:不要实现它,但如果必须,请使用=
,而不是\uuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuu
在Python3中,=
是默认情况下对==
的否定,因此您甚至不需要编写\uuu ne\uuuu
,文档也不再坚持编写一个
一般来说,对于Python3-only代码,除非需要掩盖父实现(例如,对于内置对象),否则不要编写代码
也就是说,记住Raymond Hettinger的评论:
只有在以下情况下,
\uuu ne\uuuuuuu
方法才会从\uuuu eq\uuuu
自动执行
超类中尚未定义\uuu ne\uu
。所以,如果你是
从内置继承,最好同时覆盖这两个
如果需要代码在Python2中工作,请遵循Python2的建议,它在Python3中也可以正常工作
在Python2中,Python本身不会自动实现任何与另一个操作相关的操作-因此,您应该根据==
而不是\uuuuuu eq.
来定义\uu ene.
。
例如
A类(对象):
定义(自身、其他):
返回self.value==other.value
定义(自身、其他):
return not self==其他#not `返回not self.uuu eq_uu(其他)`
看到证据了吗
- 基于
\uuu eq\uu
和 - 根本没有在Python2中实现
\uuu ne\uu
在下面的演示中提供了不正确的行为
长话短说
Python 2的文档说明:
比较运算符之间没有隐含的关系。这个
x==y的真理并不意味着
x=y
为假。因此,当
定义\uuuu eq\uuuu()
,还应定义\uu ne\uuuuu()
,以便
操作员将按预期操作
这意味着如果我们用\uuuu eq\uuu
的倒数来定义\uu ne\uuuu
,我们可以得到一致的行为
本节文档已针对Python 3进行了更新:
默认情况下,
\uuu ne\uuu()
委托给\uu eq\uu()
,并反转结果
除非未实施
在“最新消息”部分,我们看到这种行为发生了变化:
=
现在返回与==
相反的值,除非==
返回未实现
为了实现\uuu ne\uuuu
,我们更喜欢使用=
操作符而不是直接使用\uuuuuueq\uuuuu
方法,这样如果子类的self.\uuuuueq\uuuuuuuuu(其他)
返回未实现的
,对于选中的类型,Python将适当地检查其他。\uuueq\uuuuuuuuuuuuuuuuuuuuuself
来自文档:
NotImplemented
对象此类型只有一个值。只有一个对象具有此值。通过内置名称访问此对象
未实施
。数值方法和丰富的比较方法可能返回
如果不执行操作数的操作,则返回此值
假如(然后解释器将尝试反射操作,或
其他一些回退,取决于运算符。)其真值为
对
当给定一个丰富的比较运算符时,如果它们不是相同的类型,Python将检查其他
是否是一个子类型,如果定义了该运算符,则首先使用其他
的方法(与<lt;
<lt;
、
<gt;
和相反)。如果返回
NotImplemented
,则将使用相反的方法。(它不会两次检查相同的方法。)使用=
运算符允许此逻辑发生
期望值
从语义上讲,您应该在检查平等性方面实现\uu______
,因为您的类的用户希望以下函数对于一个类的所有实例都是等效的:
def否定(inst1,inst2):
“”“始终应返回与not_equals相同的值(inst1,inst2)”
返回not inst1==inst2
def不等于(inst1,inst2):
“”“始终应返回与_equals(inst1,inst2)的否定_相同的结果”
返回inst1!=inst2
也就是说,上述两个函数应始终返回相同的结果。但这取决于程序员
基于\uuuuu eq\uuuu
定义\uuuu ne\uuuuuuuu>时的意外行为演示:
首先,设置:
类baseequalable(对象):
定义初始化(self,x):
self.x=x
定义(自身、其他):
返回isinstance(其他,BaseEqualable)和self.x==other.x
类别可比性错误(基本可比):
定义(自身、其他):
返回非自我。均衡(其他)
类别可比权利(基本可比):
定义(自身、其他):
返回非self==其他
类EqMixin(对象):
定义(自身、其他):
“”“覆盖基本均衡和反弹到其他均衡,例如。
如果issubclass(类型(self),类型(other)):#在本例中为True
"""
返回未执行
类childComparableError(EqMixin,ComparableError):
“\uuuu ne\uuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuu
类别儿童可比权利(EqMixin,可比权利):
“”“使用正确的方法(使用==)”
类childCompariablePy3(EqMixin,baseequalable):
“”“否,仅在Python 3中正确。”“”
实例化非等效实例:
right1,right2=ComparableRight(1),ChildComparableRight(2)
错误1,错误2=可比错误(1),儿童可比错误(2)
right_py3_1,right_py3_2=baseequalable(1),childCompariablePy3(2)
预期行为:
(注意:虽然以下每一项的每一秒断言都是等价的,因此在逻辑上与前面的断言是多余的,但我将它们包括进来,以证明当一项是另一项的子类时,顺序并不重要。)
这些实例使用=
实现了:
断言不正确1==right2
断言非right2==right1
断言权利1!=对2
断言权利2!=对1
在Python 3下测试的这些实例也可以正常工作:
断言不正确\u py3\u 1==right\u py3\u 2
断言不正确\u py3\u 2==正确\u py3\u 1
主张权利_py3_1!=右py3 2
主张权利_py3_2!=右_py3_1
回想一下,这些都是用\uuuuuuu-ne\uuuuuuuuuuuu
实现的\uuuuuuuu-eq\uuuuuuuuu
——虽然这是预期的行为,但实现是不正确的:
断言没有错误1==error 2#这些与
断言不错误2==错误1#低于意外行为!
意外行为:
请注意,此比较与上面的比较相矛盾(非错误1==错误2
)
>&燃气轮机&燃气轮机;断言错误1!=错误2
回溯(最近一次呼叫最后一次):
文件“<;stdin>;”,第1行,在<;模块>;
断言错误
以及
>&燃气轮机&燃气轮机;断言错误2!=错误1
回溯(最近一次呼叫最后一次):
文件“<;stdin>;”,第1行,在<;模块>;
断言错误
在Python2中不要跳过\u___
有关不应跳过在Python 2中实现\u_ne\u_
的证据,请参见以下等效对象:
>&燃气轮机&燃气轮机;right_py3_1,right_py3_1儿童=基本可平等(1),儿童可比较py3(1)
&燃气轮机&燃气轮机&燃气轮机;右_py3_1!=右_py3_1child#在Python 2中进行了评估!
符合事实的
上述结果应为假
Python3源代码
的默认CPython实现在
对象的
类型对象.c
中:
案例:
/*默认情况下,_ne__()委托给_eq__()并反转结果,
除非后者返回未实现*/
如果(Py_类型(自)->;tp_richcompare==NULL){
res=未执行的Py_;
Py_增量(res);
打破
}
res=(*Py_类型(self)->;tp_-richcompare)(self、other、Py_-EQ);
if(res!=NULL&;res!=Py_未实现){
int ok=PyObject_IsTrue(res);
Py_DECREF(res);
如果(正常<;0)
res=NULL;
否则{
如果(确定)
res=Py_假;
其他的
res=Py_-True;
Py_增量(res);
}
}
打破
但是默认的\uuu ne\uuuuuuuuu
使用\uuuuu eq\uuuuuu
Python3在C级的默认实现细节使用\uuuuuuueq\uuuuuu
,因为更高级别的=
(PyObject\uRichCompare)效率更低,因此它还必须处理未实现的
如果\uuuu eq\uuuu
正确实现,那么=
的否定也是正确的-它允许我们避免低级别实现