Is it me or python that is confused with the following code ? I would expect __le__ to be called by 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
I get the same behavior with python 2.7, 3.2 and pypy 1.9.
What can I do to get __le__ called instead of __ge__ ??
The short answer is that they wanted to allow
ABto override the behavior fromA. Python can’t callAB.__lt__(a, ab), becauseamay not be a validselffor anABmethod, so instead, it callsAB.__gt__(ab, a), which is valid.The long answer is a bit more complicated.
According to the docs for rich comparison operators:
In other words,
x <= ywill cally.__ge__(x)in exactly the same cases wherex+ywould cally.__radd__(x). To compare:According to the docs for reflected operators:
So, because
XYis a subclass ofX,XY.__radd__gets preference overX.__add__. And, likewise, becauseABis a subclass ofA,AB.__ge__gets preference overA.__le__.This probably should be documented better. To figure it out, you have to ignore the parenthetical "to be used when the left argument does not support the operation but the right argument does", guess that you need to look up the normal swapped operators (there’s no link, or even mention, here), then ignore the wording that says "These functions are only called if the left operand does not support the corresponding operation", and see the "Note", which contradicts what came above… Also notice that the docs explicitly say, "There are no implied relationships among the comparison operators", only a paragraph before describing the swapped cases, which imply exactly such relationships…
Finally, this case seems odd, because
AB, rather than overriding__ge__itself, just inherited it fromB, which knows nothing aboutAand is unrelated to it. PresumablyBdidn’t intend to have its subclasses overrideA‘s behavior. But ifBwere meant to be used as a mixin forA-derived classes, maybe it would intend exactly such an override. And at any rate, the rule is probably already complicated enough without getting into where each method came from in the MRO. Whatever the reasoning, where the__ge__comes from is irrelevant; if it’s there on the subclass, it gets called.For your added final, question, "What can I do to get
__le__called instead of__ge__??"… well, you really can’t, any more than you can getX.__add__called instead ofXY.__radd__. Of course you can always implement anAB.__ge__(orXY.__radd__) that callsA.__le__(orX.__add__), but it’s presumably easier to just implementAB.__ge__in such a way that it works with anAas its other argument in the first place. Alternatively, you could remove the inheritance and find some other way to model whatever you were modeling that way. Or you could explicitly calla.__le__(ab)instead ofa<=ab. But otherwise, if you designed your classes in a way that takes advantage of the "no implied relationships" to do something weird, you were misled by the docs, and will have to redesign them somehow.