我从一个朋友那里碰到了这段代码。这是用于覆盖Python中比较方法的测试。当我运行代码时,我得到了:
是
是
是
和这个:“ TypeError:'<'在'A'和'B'的实例之间不支持”
如果是这样,为什么“ a1 == b1”没有发生相同的错误?
class A:
def __init__(self, x):
self.x = x
class B:
def __init__(self, x):
A.__init__(self, x)
def __eq__(self, other):
return self.x == other.x
def __lt__(self, other):
return self.x < other.x
a1 = A(1)
b1 = B(1)
print(b1 == a1)
print(a1 == b1)
a2 = A(2)
b2 = B(1)
print(b2 < a2)
a3 = A(1)
b3 = B(2)
print(a3 < b3)
答案 0 :(得分:2)
您还需要在类__lt__
中定义A
:
class A:
def __init__(self, x):
self.x = x
def __lt__(self, other):
return self.x < other.x
class B(A):
def __init__(self, x):
A.__init__(self, x)
def __eq__(self, other):
return self.x == other.x
def __lt__(self, other):
return self.x < other.x
a1 = A(1)
b1 = B(1)
print(b1 == a1)
print(a1 == b1)
a2 = A(2)
b2 = B(1)
print(b2 < a2)
a3 = A(1)
b3 = B(2)
print(a3 < b3)
当然,其余操作员也将保持相同。发生这种情况的原因是,在b < a
中,调用的方法是B.__lt__
;在a < b
中,调用的方法是A.__lt__
。定义了前一种方法,但没有定义。
顺便说一句,您正在A
的构造函数中调用B
的构造函数。我假设您希望B
也是A
,所以B
继承自A
。这就是为什么我的代码显示class B(A)
的原因。
答案 1 :(得分:1)
所以我更改了您的代码,将print
语句添加到__eq__
方法中,如下所示:
class A:
def __init__(self, x):
self.x = x
class B:
def __init__(self, x):
A.__init__(self, x)
def __eq__(self, other):
print('type(self) =', type(self))
print('type(other) =', type(other))
return self.x == other.x
def __lt__(self, other):
return self.x < other.x
结果是这样的:
type(self) = <class '__main__.B'>
type(other) = <class '__main__.A'>
True
type(self) = <class '__main__.B'>
type(other) = <class '__main__.A'>
True
True
Traceback (most recent call last):
File "/home/chrx/Dropbox/Documents/Programming/questions/SO_question.py", line 25, in <module>
print(a3 < b3)
TypeError: unorderable types: A() < B()
因此,即使您只为__eq__
类编写了一个B
方法,但在以相反的顺序a == b
比较时也使用了该方法。 (我相信)这是Python语言的一项功能,该功能假定相等运算符是自反的,即a == b
和b == a
的结果应相同。
但是,此属性不适用于__lt__
运算符,因为a < b
与b < a
不同。
答案 2 :(得分:1)
如果正常情况下__lt__
的比较没有意义,则无需在A
上实现A
; B
可以为A
和B
之间的比较做所有繁重的工作,但是您需要实现反映的比较运算符才能使它起作用。
这里的问题是A
没有实现__lt__
,因此Python将使用a3 < b3
的反射运算符执行B
,从而进行行测试{{ 1}}。但是您没有在b3 > a3
中实现__gt__
,因此无法反映该操作。
最简单的解决方法(通常建议您实施任何比较操作)是use functools.total_ordering
,将单个已实现的运算符扩展到整个丰富的比较套件:
B
就是这样;您的代码将正常工作,因为修饰将确保根据from functools import total_ordering
@total_ordering
class B:
... rest of B unchanged ...
/ __gt__
定义__lt__
,因此尝试进行比较的尝试将成功。
您可以等效地逐个定义每个操作,例如:
__eq__
但这很乏味且容易出错;使用class B:
... rest of class ...
def __gt__(self, other):
return self.x > other.x
def __le__(self, other):
return self.x <= other.x
def __ge__(self, other):
return self.x >= other.x
。
functools.total_ordering
测试工作得很好,因为相等是自反的,因此当另一个操作数不实现相等时,相同的重载可以在任一方向上起作用。 Python尝试==
并发现它不起作用,因此它尝试a.__eq__(b)
,因为b.__eq__(a)
在逻辑上等效于a == b
。只是比较的情况下,反映的操作使用了不同的方法。