2

私が理解していることから、のtotal_orderingデコレータfunctoolsは、順序付けられたクラスから継承されたクラスでうまく機能することが期待されていません.比較関数は既に定義されているため、定義しようとしません。

次の例を参照してください。

from functools import total_ordering
from collections import namedtuple

Test = namedtuple('Test',['a','b'])

@total_ordering
class TestOrd(Test):
    def __lt__(self,other):
        return self.b < other.b or self.b == other.b and self.a < other.a

x = TestOrd(a=1,b=2)
y = TestOrd(a=2,b=1)
print(x < y)   # Expected: False
print(x <= y)  #           False
print(x > y)   #           True
print(x >= y)  #           True
print(y < x)   #           True
print(y <= x)  #           True
print(y > x)   #           False
print(y >= x)  #           False

<すべてのテストの中で、オペレーターが関与するテストだけが期待される結果をもたらします。

クラス定義に>追加することで、同様に機能させることができます。__gt__ = lambda *_ : NotImplemented一方、__le__orに同様の定義を追加すると__ge__、対応するテストは (for __le__) で失敗します。

TypeError: unorderable types: TestOrd() <= TestOrd()

これは、これが問題に対処する適切な方法ではないと私に信じさせます.

したがって、質問: total_ordering でクラスを並べ替える適切な方法はありますか?

(はい、私はtotal_orderingの仕事を手作業で行うのは些細なことだと知っていますし、この例では unordered を定義することもnamedtuple些細なことだと知っています。)

4

2 に答える 2

2

の実装をtotal_ordering見ると、次のような問題があることがわかります。

roots = [op for op in _convert if getattr(cls, op, None) is not getattr(object, op, None)]

clsこれは、 で定義されたバージョンが から継承されたものではないことを注意深くチェックしますobjectが、他の継承されたメソッドは含まれます (つまり、置き換えられません)。total_reordering最小限の微調整は、代わりに次を使用する独自のコピーを定義することです (私はそれを と呼びました):

roots = set(cls.__dict__) & set(_convert)

(以前の実装に基づく)。これは、クラスで直接定義されたメソッドのみを参照するため、デコレーターは継承されたバージョンをオーバーライドします。これにより、最初に期待した結果が得られます。

False
False
True
True
True
True
False
False

次の定義を誤解していることに注意してください。

 __gt__ = lambda *_ : NotImplemented

します; デコレーターの動作は変更されません (この場合は何も変わりません)。継承されたバージョンをオーバーライドし、>実行時に他のメソッドに委任されるようにするだけです。

于 2015-09-30T10:16:04.873 に答える