繁体   English   中英

在Python中覆盖比较方法

[英]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 == bb == a应该具有相同的结果。

但是,此属性不适用于这种情况下的__lt__运算符,因为a < bb < a不同。

如果比较通常对A没有意义,则__lt__A上实现__lt__ B可以为AB之间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.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM