1. ホーム
  2. python

[解決済み] cmp__の代わりに__lt__を使用する。

2022-10-16 15:45:06

質問

Python 2.xでは比較演算子をオーバーロードする方法が2つあります。 __cmp__ または、次のようなリッチな比較演算子です。 __lt__ . リッチ比較のオーバーロードが好ましいと言われていますが、なぜでしょうか?

リッチ比較演算子は、それぞれを実装するのは簡単ですが、ほぼ同じロジックでいくつも実装しなければなりません。 しかし、もしビルトインの cmp とタプルオーダーがあれば __cmp__ は非常にシンプルになり、すべての比較を満たすようになります。

class A(object):
  def __init__(self, name, age, other):
    self.name = name
    self.age = age
    self.other = other
  def __cmp__(self, other):
    assert isinstance(other, A) # assumption for this example
    return cmp((self.name, self.age, self.other),
               (other.name, other.age, other.other))

このシンプルさは、リッチな比較の6つすべて(!)をオーバーロードするよりもずっと私のニーズを満たしているように思えます。 (ただし、"swapped argument"/reflected 動作に依存する場合、それを 4 つに減らすことができますが、私の謙虚な意見では、それは複雑さの純増という結果になります)。

をオーバーロードするだけなら、私が注意しなければならない予期せぬ落とし穴はありますか? __cmp__ ?

を理解しています。 < , <= , == などの演算子は、他の目的のためにオーバーロードすることができ、好きなオブジェクトを返すことができます。 私はそのアプローチの利点について尋ねているのではなく、これらの演算子を数値のために意味するのと同じ意味で比較のために使用する場合の違いについてだけ尋ねています。

更新しました。

クリストファーとして が指摘したように , cmp が 3.x では消えている。 のように簡単に比較の実装ができる代替手段はないのでしょうか? __cmp__ ?

どのように解決するのですか?

そうですね、すべてを実装するのは簡単で、例えば __lt__ をミキシンクラス(またはメタクラス、またはクラスデコレーター)で実装するのは簡単です。

例えば

class ComparableMixin:
  def __eq__(self, other):
    return not self<other and not other<self
  def __ne__(self, other):
    return self<other or other<self
  def __gt__(self, other):
    return other<self
  def __ge__(self, other):
    return not self<other
  def __le__(self, other):
    return not other<self

これで、あなたのクラスでは __lt__ を定義し、ComparableMixin を多重継承することができます(もしあれば、他のベースが必要な後に)。クラス デコレーターは非常に似ていて、装飾する新しいクラスの属性として同様の関数を挿入するだけです (その結果、実行時に微視的に速くなり、メモリの点では同様に微小なコストとなるかもしれません)。

もちろん、もしあなたのクラスが(例えば) __eq____ne__ を定義する場合、ミキシンのバージョンを使用しないように直接定義する必要があります(たとえば dict のように) -- 実際には __ne__ として、それを容易にするために定義されるかもしれません。

def __ne__(self, other):
  return not self == other

のみで構成されていますが、上のコードでは、対称性を保つために < ;-). なぜ __cmp__ は行かなければならなかったのかというと、私たちは した ある __lt__ といった具合に、まったく同じことをするのになぜ別の別の方法を持ち続けるのでしょうか? すべてのPythonランタイム(Classic, Jython, IronPython, PyPy, ...)において、これはとてもデッドウェイトなのです。 そのコードは は間違いなく C言語ではISO標準の"Spirit of C"のセクションで同じ原則を掲げています)。

これは、私たちがわざわざ禁止していることを意味するものではありませんが (たとえば、いくつかの用途における mixin とクラス装飾の間のほぼ等価性)、それは間違いなく を行います。 というのは、まったく同じタスクを実行するために複数の同等のアプローチをサポートするために、コンパイラやランタイムに冗長に存在するコードを持ち歩くことは好ましくないからです。

さらなる編集:多くのクラスに対して比較とハッシュを提供するさらに良い方法が実際に存在し、質問の中のそれを含みます。 __key__ メソッドです。私はそのためのPEPを書くことができなかったので、あなたがそれを好きなら、現在ミキシン(&c)でそれを実装する必要があります。

class KeyedMixin:
  def __lt__(self, other):
    return self.__key__() < other.__key__()
  # and so on for other comparators, as above, plus:
  def __hash__(self):
    return hash(self.__key__())

インスタンスの他のインスタンスとの比較は、いくつかのフィールドを持つそれぞれのタプルを比較することに集約されるのが非常に一般的なケースです -- そして、ハッシュもまったく同じ基盤で実装されるべきです。その __key__ は、そのニーズに直接応える特別な方法です。