我或python是否与以下代码混淆?我希望__le__被召唤a <= ab,而不是__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
Run Code Online (Sandbox Code Playgroud)
我得到了与python 2.7,3.2和pypy 1.9相同的行为.
我该怎么办才能被__le__召唤而不是__ge__?
简短的回答是他们想要允许AB覆盖行为A.Python无法调用AB.__lt__(a, ab),因为它a可能不是一个有效self的AB方法,所以相反,它调用AB.__gt__(ab, a),这是有效的.
长的答案有点复杂.
根据丰富的比较运营商的文档:
这些方法没有交换参数版本(当左参数不支持操作但右参数支持时使用); 相反,
__lt__()和__gt__()彼此的思考,__le__()并__ge__()在对方的反映,__eq__()并__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
Run Code Online (Sandbox Code Playgroud)
根据反映运营商的文档:
调用这些方法来实现具有反射(交换)操作数的二进制算术运算.仅当左操作数不支持相应操作且操作数具有不同类型时才调用这些函数...
注意:如果右操作数的类型是左操作数类型的子类,并且该子类提供了操作的反射方法,则此方法将在左操作数的非反射方法之前调用.此行为允许子类覆盖其祖先的操作.
所以,因为XY是的一个子类X,XY.__radd__得到优先于X.__add__.而且,同样,因为AB是一个子类A,AB.__ge__得到优先权A.__le__.
这可能应该更好地记录.要想出来,你必须忽略括号"当左参数不支持操作但右参数支持时使用",猜测你需要查找正常的交换操作符(没有链接,甚至没有提到,这里),然后忽略"只有在左操作数不支持相应操作时才调用这些函数"的措辞,并看到"注意",这与上面提到的内容相矛盾...另请注意文档明确说"比较运算符之间没有隐含的关系",在描述交换的情况之前只有一个段落,这意味着这种关系......
最后,这个案例看起来很奇怪,因为AB,而不是覆盖__ge__自己,只是从B它继承而来,它对它一无所知A,与之无关.据推测B,并不打算让它的子类覆盖其A行为.但是,如果它B被用作混合A类的衍生类,也许它会打算完全这样的覆盖.无论如何,规则可能已经足够复杂,而没有进入MRO中每个方法的来源.无论推理__ge__是什么,来自哪里是无关紧要的; 如果它在子类上,它会被调用.
对于你添加的最后一个问题,"我能做些什么才能被__le__召唤而不是__ge__?"......好吧,你真的不能,除了你可以被X.__add__召唤而不是XY.__radd__.当然,你总是可以实现一个AB.__ge__(或XY.__radd__)调用A.__le__(或X.__add__),但它可能更容易实现AB.__ge__,因为它首先与A其他参数一起工作.或者,您可以删除继承并找到一些其他方式来模拟您以这种方式建模的任何内容.或者您可以明确地调用a.__le__(ab)而不是a<=ab.但除此之外,如果你以一种利用"无隐含关系"的方式设计你的课程来做一些奇怪的事情,你就被文档误导了,并且必须以某种方式重新设计它们.
| 归档时间: |
|
| 查看次数: |
915 次 |
| 最近记录: |