6

次のコードと混同しているのは私ですか、それとも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

Python 2.7、3.2、および pypy 1.9 でも同じ動作が得られます。

__le__代わりに呼び出されるにはどうすればよいです__ge__か??

4

1 に答える 1

9

AB短い答えは、彼らがからの動作をオーバーライドできるようにしたかったということAです。はメソッドに対して有効でない可能性があるAB.__lt__(a, ab)ため、Python は を呼び出すことができません。代わりに、有効な を呼び出します。aselfABAB.__gt__(ab, a)

長い答えはもう少し複雑です。

リッチ比較演算子のドキュメントによると:

これらのメソッドには、引数を交換したバージョンはありません (左の引数が操作をサポートしていないが、右の引数が操作をサポートしている場合に使用されます)。むしろ、__lt__()__gt__()はお互いの反映であり、__le__()__ge__()はお互いの反映であり、__eq__()__ne__()は自分自身の反映です。

言い換えると、x <= ywill が を呼び出す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

反映された演算子のドキュメントによると:

これらのメソッドは、反映された (交換された) オペランドを使用して、バイナリ算術演算を実装するために呼び出されます。これらの関数は、左側のオペランドが対応する操作をサポートしておらず、オペランドの型が異なる場合にのみ呼び出されます…</p>

: 右側のオペランドの型が左側のオペランドの型のサブクラスであり、そのサブクラスが操作の反映されたメソッドを提供する場合、このメソッドは左側のオペランドの反映されていないメソッドの前に呼び出されます。この動作により、サブクラスは祖先の操作をオーバーライドできます。

したがって、XYは のサブクラスであるためXXY.__radd__は よりも優先されX.__add__ます。同様に、ABは のサブクラスであるためAAB.__ge__は よりも優先されA.__le__ます。

これはおそらく、より適切に文書化する必要があります。それを理解するには、「左の引数が操作をサポートしていないが、右の引数がサポートしている場合に使用する」という括弧を無視する必要があります。 、ここでは)、「これらの関数は、左のオペランドが対応する操作をサポートしていない場合にのみ呼び出される」という文言を無視し、上記の内容と矛盾する「注」を参照してください…また、ドキュメントが明示的に言っていることに注意してください。比較演算子間に暗黙の関係はありません」、交換されたケースを説明する前の段落のみで、まさにそのような関係を暗示しています…</p>

最後に、このケースは奇妙に思えます。なぜならAB、 はそれ自体をオーバーライドするのではなく、それについて何も知らず、関係のない__ge__から継承したからです。おそらく、そのサブクラスで の動作をオーバーライドするつもりはありませんでした。しかし、派生クラスのミックスインとして使用することを意図していた場合、おそらくそのようなオーバーライドを意図しているでしょう。いずれにせよ、MRO で各メソッドがどこから来たのかを説明しなくても、ルールはすでに十分に複雑になっている可能性があります。理由が何であれ、 がどこから来たのかは関係ありません。サブクラスにある場合は、呼び出されます。BABABA__ge__

追加された最終的な質問については、「の代わりに呼び出されるにはどうすればよいですか??」…まあ、__le__代わりに呼び出されるのと同じように__ge__、実際にはできません。もちろん、いつでも(or ) を呼び出す(or ) を実装できますが、そもそも他の引数として を使用して動作するように実装する方がおそらく簡単です。または、継承を削除して、その方法でモデル化していたものをモデル化する別の方法を見つけることもできます。または、代わりに明示的に呼び出すことができますX.__add__XY.__radd__AB.__ge__XY.__radd__A.__le__X.__add__AB.__ge__Aa.__le__(ab)a<=ab. しかし、そうでなければ、「暗黙の関係がない」ことを利用して奇妙なことをするようにクラスを設計した場合、ドキュメントに惑わされ、何らかの形で再設計する必要があります。

于 2012-12-10T11:02:56.853 に答える