在Python中覆盖比较方法

时间:2018-12-18 20:58:31

标签: python python-3.x

我从一个朋友那里碰到了这段代码。这是用于覆盖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)    

3 个答案:

答案 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 == bb == a的结果应相同。

但是,此属性不适用于__lt__运算符,因为a < bb < a不同。

答案 2 :(得分:1)

如果正常情况下__lt__的比较没有意义,则无需在A上实现AB可以为AB之间的比较做所有繁重的工作,但是您需要实现反映的比较运算符才能使它起作用。

这里的问题是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。只是比较的情况下,反映的操作使用了不同的方法。