python 与 __le__、__ge__ 的错误?

2023-11-25

是我还是Python对以下代码感到困惑?我希望__le__被称为a <= ab, not __ge__:

#!/usr/bin/env python2

class B(object):
    def __ge__(self, other):
        print("__ge__ unexpectedly called")

class A(object):
    def __le__(self, other):
        print("__le__ called")

class AB(A, B):
    pass

a = A()
ab = AB()

a <= ab # --> __ge__ unexpectedly called
ab <= a # --> __le__ called

我在 python 2.7、3.2 和 pypy 1.9 中得到了相同的行为。

我该怎么做才能得到__le__调用而不是__ge__ ??


简短的回答是他们希望允许AB覆盖行为A。 Python无法调用AB.__lt__(a, ab), 因为a可能不是有效的self for an AB方法,因此它调用AB.__gt__(ab, a),这是有效的。

长答案有点复杂。

根据文档丰富的比较运算符:

这些方法没有交换参数版本(当左侧参数不支持该操作但右侧参数支持该操作时使用);相当,__lt__() and __gt__()都是彼此的倒影,__le__() and __ge__()是彼此的反映,并且__eq__() and __ne__()是他们自己的反映。

换句话说,x <= y将会通知y.__ge__(x)在完全相同的情况下x+y会打电话y.__radd__(x)。比较:

>>> class X(object):
...     def __add__(self, other):
...         print('X.add')
>>> class Y(object):
...     def __radd__(self, other):
...         print('Y.radd')
>>> class XY(X, Y):
...     pass
>>> x, xy = X(), XY()
>>> x + xy
Y.radd

根据文档反射算子:

调用这些方法来实现带有反射(交换)操作数的二进制算术运算。仅当左操作数不支持相应操作且操作数类型不同时才会调用这些函数......

Note:如果右操作数的类型是左操作数类型的子类,并且该子类为该操作提供了反射方法,则该方法将在左操作数的非反射方法之前被调用。这种行为允许子类覆盖其祖先的操作。

所以,因为XY是一个子类X, XY.__radd__优先于X.__add__。并且,同样,因为AB是一个子类A, AB.__ge__优先于A.__le__.

这可能应该更好地记录下来。要弄清楚这一点,您必须忽略括号“当左参数不支持该操作但右参数支持该操作时使用”,猜测您需要查找正常的交换运算符(没有链接,甚至提到,此处),然后忽略“仅当左操作数不支持相应操作时才调用这些函数”的措辞,并参见“注释”,这与上面的内容相矛盾......还要注意文档明确指出,“比较运算符之间没有隐含的关系”,只有在描述交换的情况之前的一段,这暗示了正是这样的关系......

最后,这个案例看起来很奇怪,因为AB,而不是覆盖__ge__本身,只是继承自B,它一无所知A并且与之无关。想必B不打算让其子类覆盖A的行为。但如果B旨在用作 mixinA- 派生类,也许吧would正是打算这样的覆盖。无论如何,如果不考虑 MRO 中每种方法的来源,该规则可能已经足够复杂了。无论出于何种原因,__ge__来自是无关紧要的;如果它存在于子类中,则会调用它。

对于您添加的最后一个问题,“我该怎么做才能获得__le__调用而不是__ge__??“……好吧,你真的不能,比你能得到的更多X.__add__调用而不是XY.__radd__。当然,您始终可以实施AB.__ge__ (or XY.__radd__) 调用A.__le__ (or X.__add__),但实施起来可能更容易AB.__ge__以这样的方式,它可以与A首先作为它的另一个论点。或者,您可以删除继承并找到其他方法来对您以这种方式建模的任何内容进行建模。或者你可以明确地调用a.__le__(ab)代替a<=ab。但除此之外,如果您以利用“无隐含关系”的方式设计类来做一些奇怪的事情,那么您就会被文档误导,并且必须以某种方式重新设计它们。

本文内容由网友自发贡献,版权归原作者所有,本站不承担相应法律责任。如您发现有涉嫌抄袭侵权的内容,请联系:hwhale#tublm.com(使用前将#替换为@)

python 与 __le__、__ge__ 的错误? 的相关文章

随机推荐