Lau*_*RTE 11 python operators python-2.7
我正在玩__eq__
操作员和NotImplemented
价值.
我试图了解obj1.__eq__(obj2)
返回时会发生什么NotImplemented
,obj2.__eq__(obj1)
也会返回NotImplemented
.
根据为什么返回NotImplemented而不是引发NotImplementedError的答案,以及详细的文章如何在"LiveJournal"博客中覆盖Python中的比较运算符,运行时应该回归到内置行为(基于身份==
和!=
).
但是,尝试下面的示例,似乎我__eq__
对每对对象都有多个调用.
class Apple(object):
def __init__(self, color):
self.color = color
def __repr__(self):
return "<Apple color='{color}'>".format(color=self.color)
def __eq__(self, other):
if isinstance(other, Apple):
print("{self} == {other} -> OK".format(self=self, other=other))
return self.color == other.color
print("{self} == {other} -> NotImplemented".format(self=self, other=other))
return NotImplemented
class Orange(object):
def __init__(self, usage):
self.usage = usage
def __repr__(self):
return "<Orange usage='{usage}'>".format(usage=self.usage)
def __eq__(self, other):
if isinstance(other, Orange):
print("{self} == {other}".format(self=self, other=other))
return self.usage == other.usage
print("{self} == {other} -> NotImplemented".format(self=self, other=other))
return NotImplemented
>>> apple = Apple("red")
>>> orange = Orange("juice")
>>> apple == orange
<Apple color='red'> == <Orange usage='juice'> -> NotImplemented
<Orange usage='juice'> == <Apple color='red'> -> NotImplemented
<Orange usage='juice'> == <Apple color='red'> -> NotImplemented
<Apple color='red'> == <Orange usage='juice'> -> NotImplemented
False
Run Code Online (Sandbox Code Playgroud)
我希望只有:
<Apple color='red'> == <Orange usage='juice'> -> NotImplemented
<Orange usage='juice'> == <Apple color='red'> -> NotImplemented
Run Code Online (Sandbox Code Playgroud)
然后回到身份比较id(apple) == id(orange)
- > False
.
这是Python跟踪器中的问题#6970 ; 它在2.7和Python 3.0和3.1中保持不变.
这是由两个地方同时尝试直接和交换比较引起的,当__eq__
执行两个自定义类与方法之间的比较时.
丰富的比较通过该PyObject_RichCompare()
函数,对于具有不同类型(间接)委托的对象try_rich_compare()
.在这个函数中v
,它w
是左右操作数对象,并且由于两者都有一个__eq__
方法,因此函数调用v->ob_type->tp_richcompare()
和w->ob_type->tp_richcompare()
.
对于自定义类,tp_richcompare()
槽被定义为slot_tp_richcompare()
函数,并且此函数再次__eq__
针对双方执行,self.__eq__(self, other)
然后再执行other.__eq__(other, self)
.
最后,这意味着apple.__eq__(apple, orange)
并且orange.__eq__(orange, apple)
被调用第一次尝试try_rich_compare()
,然后调用反向,导致orange.__eq__(orange, apple)
和apple.__eq__(apple, orange)
调用self
和other
交换slot_tp_richcompare()
.
请注意,问题仅限于两个类定义__eq__
方法的不同自定义类的实例.如果任何一方没有这样的方法__eq__
只执行一次:
>>> class Pear(object):
... def __init__(self, purpose):
... self.purpose = purpose
... def __repr__(self):
... return "<Pear purpose='{purpose}'>".format(purpose=self.purpose)
...
>>> pear = Pear("cooking")
>>> apple == pear
<Apple color='red'> == <Pear purpose='cooking'> -> NotImplemented
False
>>> pear == apple
<Apple color='red'> == <Pear purpose='cooking'> -> NotImplemented
False
Run Code Online (Sandbox Code Playgroud)
如果你有两个相同类型和__eq__
返回的实例NotImplemented
,你甚至可以得到六个比较:
>>> class Kumquat(object):
... def __init__(self, variety):
... self.variety = variety
... def __repr__(self):
... return "<Kumquat variety=='{variety}'>".format(variety=self.variety)
... def __eq__(self, other):
... # Kumquats are a weird fruit, they don't want to be compared with anything
... print("{self} == {other} -> NotImplemented".format(self=self, other=other))
... return NotImplemented
...
>>> Kumquat('round') == Kumquat('oval')
<Kumquat variety=='round'> == <Kumquat variety=='oval'> -> NotImplemented
<Kumquat variety=='oval'> == <Kumquat variety=='round'> -> NotImplemented
<Kumquat variety=='round'> == <Kumquat variety=='oval'> -> NotImplemented
<Kumquat variety=='oval'> == <Kumquat variety=='round'> -> NotImplemented
<Kumquat variety=='oval'> == <Kumquat variety=='round'> -> NotImplemented
<Kumquat variety=='round'> == <Kumquat variety=='oval'> -> NotImplemented
False
Run Code Online (Sandbox Code Playgroud)
第一组两次比较是从优化尝试中调出来的; 当两个实例具有相同的类型时,您只需要调用v->tp_richcompare(v, w)
并且可以跳过强制(对于数字).但是,当该比较失败(NotImplemented
返回)时,也会尝试标准路径.
如何在Python 2中进行比较变得相当复杂,因为__cmp__
仍需要支持旧的3向比较方法; 在Python 3中,支持__cmp__
删除,更容易解决问题.因此,修复程序从未向后移植到2.7.