我偶然从我的一个朋友那里看到了这段代码。它是对重写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)
发布于 2018-12-19 05:20:01
如果比较通常对A
没有意义,那么您不需要在A
上实现__lt__
;B
可以完成A
和B
之间比较的所有繁重任务,但您需要实现反射的比较运算符才能正常工作。
这里的问题是A
没有实现__lt__
,所以Python将使用来自B
的反射操作符执行a3 < b3
,从而使行测试成为b3 > a3
。但是您没有在B
中实现__gt__
,因此无法反映该操作。
最简单的解决方法(如果您实现了任何比较操作,通常建议使用use 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
。只有在反射操作使用不同方法的情况下,才会进行丰富的比较。
发布于 2018-12-19 05:04:08
您还需要在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)
。
发布于 2018-12-19 05:11:33
所以我修改了代码,将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
。
https://stackoverflow.com/questions/53840957
复制相似问题