[英]Overiding comparison methods in Python
我遇到了一位朋友的这段代码。 这是用于覆盖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)
您还需要在类A
定义__lt__
:
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__
的方法。 定义了前一种方法,但没有定义。
顺便说一句,您正在B
的构造函数中调用A
的构造函数。 我假设您希望B
也是A
,因此B
继承自A
这就是为什么我的代码说class B(A)
的原因。
因此,我更改了代码,将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()
因此,即使您只为B
类编写了一个__eq__
方法,也以相反的顺序a == b
。 (我相信)这是Python语言的一项功能,该功能假定相等运算符是自反的,即a == b
和b == a
应该具有相同的结果。
但是,此属性不适用于这种情况下的__lt__
运算符,因为a < b
与b < a
不同。
如果比较通常对A
没有意义,则__lt__
在A
上实现__lt__
; B
可以为A
和B
之间A
比较完成所有繁重的工作,但是您需要实现反映的比较运算符才能使它起作用。
这里的问题是A
没有实现__lt__
,因此Python将使用B
的反射运算符执行a3 < b3
,从而使行测试b3 > a3
。 但是您没有在B
实现__gt__
,因此无法反映该操作。
最简单的解决方法(通常建议您实施任何比较操作)是使用functools.total_ordering
将单个已实现的运算符扩展为整个丰富的比较套件:
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
在逻辑上等同于b == a
。 只有在比较操作中反映的操作使用不同的方法时,才可以进行比较。
声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.