#!/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__?
解决方法
长的答案有点复杂.
根据rich comparison operators的文档:
There are no swapped-argument versions of these methods (to be used when the left argument does not support the operation but the right argument does); rather,
__lt__()
and__gt__()
are each other’s reflection,__le__()
and__ge__()
are each other’s reflection,and__eq__()
and__ne__()
are their own reflection.
换句话说,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
These methods are called to implement the binary arithmetic operations… with reflected (swapped) operands. These functions are only called if the left operand does not support the corresponding operation and the operands are of different types…
Note: If the right operand’s type is a subclass of the left operand’s type and that subclass provides the reflected method for the operation,this method will be called before the left operand’s non-reflected method. This behavior allows subclasses to override their ancestors’ operations.
因此,因为XY是X的子类,所以XY .__ radd__优先于X .__ add__.同样,因为AB是A的子类,AB .__ ge__优先于A .__ le__.
这可能应该更好地记录.要想出来,你必须忽略括号“当左参数不支持操作但右参数支持时使用”,猜测你需要查找正常的交换操作符(没有链接,甚至没有提到,这里),然后忽略“只有在左操作数不支持相应操作时才调用这些函数”的措辞,并看到“注意”,这与上面提到的内容相矛盾…另请注意文档明确说“比较运算符之间没有隐含的关系“,在描述交换的情况之前只有一个段落,这意味着这种关系……
最后,这个案例看起来很奇怪,因为AB而不是覆盖__ge__本身,只是从B继承它,它对A一无所知,与它无关.据推测,B并不打算让它的子类覆盖A的行为.但是如果B本来要用作A派生类的mixin,那么它可能就是这样的覆盖.无论如何,规则可能已经足够复杂,而没有进入MRO中每个方法的来源.无论什么原因,__ge__的来源都是无关紧要的;如果它在子类上,它会被调用.
对于你添加的最后一个问题,“我能做些什么来调用__le__而不是__ge__ ??”……好吧,你真的不能,除了你可以得到X .__ add__而不是XY .__ radd__.当然你总是可以实现一个调用A .__ le __(或X .__ add__)的AB .__ ge __(或XY .__ radd__),但是它可能更容易实现AB .__ ge__,这样它就可以用A作为它其他论点首先.或者,您可以删除继承并找到一些其他方式来模拟您以这种方式建模的任何内容.或者你可以明确地调用.__ le __(ab)而不是< = ab.但除此之外,如果你以一种利用“无隐含关系”的方式设计你的课程来做一些奇怪的事情,你就被文档误导了,并且必须以某种方式重新设计它们.