web-dev-qa-db-ja.com

Python)の比較演算子と「豊富な比較」メソッド

誰かが私に2つの違いを説明できますか?それらは通常同等ですか?ここでは完全に間違っているかもしれませんが、各比較演算子は必然的に1つの「豊富な比較」メソッドに関連していると思いました。これはドキュメントからです:

演算子記号とメソッド名の対応は次のとおりです。

x<yx.__lt__(y)を呼び出し、x<=yx.__le__(y)を呼び出し、x==yx.__eq__(y)を呼び出し、x!=yx.__ne__(y)を呼び出し、x>yx.__gt__(y)を呼び出し、x>=yx.__ge__(y)を呼び出します。

これが私の混乱を示す例です。

Python 3.x:

dict1 = {1:1}
dict2 = {2:2}

>>> dict1 < dict2
Traceback (most recent call last):
  File "<stdin>", line 1, in <module>
TypeError: '<' not supported between instances of 'dict' and 'dict'
>>> dict1.__lt__(dict2)
NotImplemented

Python 2.x:

dict1 = {1:1}
dict2 = {2:2}

>>> dict1 < dict2
True
>>> dict1.__lt__(dict2)
NotImplemented

python 3の例から、dict1 < dict2の呼び出しはサポートされていないようですが、Python 2の例はどうですか?なぜ受け入れられるのですか?

Python 2、in Python 3では、すべてのオブジェクトが比較演算子をサポートしているわけではありません。しかし、驚いたことに、どちらのバージョンも__lt__()を呼び出すとNotImplementedシングルトンを返します。 。

13
scharette

これは、___cmp___マジックメソッドに依存しています。これは、リッチ比較演算子が置き換えることを意図したものです。

_>>> dict1 = {1:1}
>>> dict2 = {2:2}
>>> dict1.__cmp__
<method-wrapper '__cmp__' of dict object at 0x10f075398>
>>> dict1.__cmp__(dict2)
-1
_

順序logicに関しては、ここにPython 2.7 ドキュメント

マッピング(dictのインスタンス)は、(キー、値)のペアが等しい場合にのみ、等しいと比較されます。キーと値の同等性の比較により、再帰性が強化されます。

平等以外の結果は一貫して解決されますが、それ以外の定義はありません。

脚注付き:

以前のバージョンのPythonは、並べ替えられた(キー、値)リストの辞書式比較を使用していましたが、同等性を比較する一般的なケースでは非常にコストがかかりました。さらに以前のバージョンのPythonは、辞書をIDのみで比較しましたが、{}と比較することで辞書の空をテストできると期待されていたため、これは驚きをもたらしました。

また、Python 3.0では、順序が簡略化されています。これは ドキュメント からのものです。

順序比較演算子_(<, <=, >=, >)_は、オペランドに意味のある自然順序がない場合にTypeError例外を発生させます。

builtin.sorted()およびlist.sort()は、比較関数を提供するcmp引数を受け入れなくなりました。代わりにkey引数を使用してください。

cmp()関数はなくなったものとして扱われる必要があり、__cmp__()特殊メソッドはサポートされなくなりました。ソートには__lt__()を使用し、__eq__()__hash__()を使用し、必要に応じてその他の豊富な比較を行います。 (本当にcmp()機能が必要な場合は、式_(a > b) - (a <> b)_をcmp(a, b)と同等のものとして使用できます。)

したがって、明示的に言うと、Python 2では、豊富な比較演算子が実装されていないため、dictオブジェクトはデータから___cmp___にフォールバックします-モデル ドキュメント

object.__cmp__(self, other)
リッチ比較(上記を参照)が定義されていない場合、比較操作によって呼び出されます。 self <otherの場合は負の整数、self == otherの場合はゼロ、self> otherの場合は正の整数を返す必要があります。

7

演算子に関する注意<__lt__

import types

class A:
    def __lt__(self, other): return True

def new_lt(self, other): return False

a = A()
print(a < a, a.__lt__(a))  # True True
a.__lt__ = types.MethodType(new_lt, a)
print(a < a, a.__lt__(a))  # True False
A.__lt__ = types.MethodType(new_lt, A)
print(a < a, a.__lt__(a))  # False False

<呼び出し__lt__クラスで定義されています。 __lt__呼び出し__lt__オブジェクトで定義されています。

それは通常同じです:)そしてそれを使うのは完全においしいです:A.__lt__ = new_lt

1
Ivo