首页
学习
活动
专区
工具
TVP
发布
社区首页 >问答首页 >覆盖Python中的比较方法

覆盖Python中的比较方法
EN

Stack Overflow用户
提问于 2018-12-19 04:58:31
回答 3查看 88关注 0票数 2

我偶然从我的一个朋友那里看到了这段代码。它是对重写Python中的比较方法的测试。当我运行代码时,我得到了这样的结果:

真的

真的

真的

这样:"TypeError:'<‘在'A’和‘B’的实例之间不受支持“

如果是这样的话,为什么"a1 == b1“没有发生同样的错误呢?

代码语言:javascript
复制
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)    
EN

回答 3

Stack Overflow用户

回答已采纳

发布于 2018-12-19 05:20:01

如果比较通常对A没有意义,那么您不需要在A上实现__lt__B可以完成AB之间比较的所有繁重任务,但您需要实现反射的比较运算符才能正常工作。

这里的问题是A没有实现__lt__,所以Python将使用来自B的反射操作符执行a3 < b3,从而使行测试成为b3 > a3。但是您没有在B中实现__gt__,因此无法反映该操作。

最简单的解决方法(如果您实现了任何比较操作,通常建议使用use functools.total_ordering )是将单个实现的运算符扩展到整个丰富的比较套件:

代码语言:javascript
复制
from functools import total_ordering

@total_ordering
class B:
     ... rest of B unchanged ...

就是这样;您的代码将会正常工作,因为该修饰将确保__gt__是按照__lt__/__eq__定义的,因此反转比较的尝试将会成功。

您可以逐个等效地定义每个操作,例如:

代码语言:javascript
复制
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。只有在反射操作使用不同方法的情况下,才会进行丰富的比较。

票数 1
EN

Stack Overflow用户

发布于 2018-12-19 05:04:08

您还需要在A类中定义__lt__

代码语言:javascript
复制
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)

票数 2
EN

Stack Overflow用户

发布于 2018-12-19 05:11:33

所以我修改了代码,将print语句添加到__eq__方法中,如下所示:

代码语言:javascript
复制
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

结果是:

代码语言:javascript
复制
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 < b不同于b < a

票数 1
EN
页面原文内容由Stack Overflow提供。腾讯云小微IT领域专用引擎提供翻译支持
原文链接:

https://stackoverflow.com/questions/53840957

复制
相关文章

相似问题

领券
问题归档专栏文章快讯文章归档关键词归档开发者手册归档开发者手册 Section 归档