web-dev-qa-db-ja.com

なぜPythonでは逆のタイプのソートが行われるのですか?

reversedのタイプは「タイプ」です:

>>> type(reversed)
<class 'type'>

sortedのタイプは「組み込み関数またはメソッド」です:

>>> type(sorted)
<class 'builtin_function_or_method'>

ただし、実際には同じように見えます。機能の明らかな違い(逆順と並べ替えの順序)を除いて、この実装の違いの理由は何ですか?

39
user8445949

違いは、reversedはイテレータ(遅延評価でもある)であり、sortedは「熱心に」機能する関数であることです。

mapZipfilterreversed、...のようなすべての組み込みイテレータ(少なくともpython-3.xでは)は次のように実装されますクラス。熱心に動作する組み込み関数は関数ですが、 minmaxanyallおよびsorted

>>> a = [1,2,3,4]
>>> r = reversed(a)
<list_reverseiterator at 0x2187afa0240>

実際には、値を取得するためにイテレータを「消費」する必要があります(例:list):

>>> list(r)
[4, 3, 2, 1]

一方、この「消費」部分は、sortedのようなfunctionsには必要ありません。

>>> s = sorted(a)
[1, 2, 3, 4]

コメントでは、これらが関数ではなくクラスとして実装されている理由が尋ねられました。答えるのは簡単ではありませんが、最善を尽くします。

遅延評価操作を使用すると、大きな利点が1つあります。それらを連鎖させると、メモリ効率が非常に高くなります。明示的に「要求」されない限り、中間リストを作成する必要はありません。これが、mapZip、およびfilterが、熱心に動作する関数(python-2.x)から遅延動作するクラス(python-3.x)に変更された理由です。 )。

一般にPythonでイテレータを作成するには2つの方法があります:

  • return selfメソッドに__iter__を含むクラス
  • ジェネレータ関数-yieldを含む関数

ただし(少なくともCPython)はすべての組み込み(およびいくつかの標準ライブラリモジュール)をCで実装します。Cで反復子クラスを作成するのは非常に簡単ですが、Python-Cに基づいてジェネレーター関数を作成する適切な方法は見つかりませんでした-API。したがって、これらのイテレーターがクラスとして(CPythonで)実装される理由は、単に便利であるか、(高速または実装可能な)代替手段がないためかもしれません。

ジェネレータの代わりにクラスを使用するもう1つの理由があります。クラスに特別なメソッドを実装できますが、ジェネレータ関数に実装することはできません。それは印象的に聞こえないかもしれませんが、それは明確な利点があります。たとえば、ほとんどのイテレータは __reduce__ および __setstate__ を使用して pickled (少なくともPython-3.xでは)にすることができます=メソッド。つまり、それらをディスクに保存して、コピーすることができます。 Python-3.4以降、一部のイテレータは __length_hint__ も実装します。これにより、これらのイテレータをlist(および類似の)でより高速に使用できます。


reversedは(iterのように)ファクトリ関数として簡単に実装できますが、iterとは異なり、2つの一意のクラス、reversedは、1つの一意のクラスのみを返すことができます。

可能な(そして一意の)クラスを説明するには、 __iter__ メソッドがなく、 __reversed__ メソッドがないが、反復可能で逆のクラスを検討する必要があります。 -iterable( __getitem__ および __len__ を実装することにより):

class A(object):
    def __init__(self, vals):
        self.vals = vals

    def __len__(self):
        return len(self.vals)

    def __getitem__(self, idx):
        return self.vals[idx]

また、iterの場合は抽象化レイヤー(ファクトリー関数)を追加するのが理にかなっていますが、返されるクラスは入力引数の数に依存するためです。

>>> iter(A([1,2,3]))
<iterator at 0x2187afaed68>
>>> iter(min, 0)   # actually this is a useless example, just here to see what it returns
<callable_iterator at 0x1333879bdd8>

その推論はreversedには適用されません。

>>> reversed(A([1,2,3]))
<reversed at 0x2187afaec50>
51
MSeifert

reversedsortedの違いは何ですか?

興味深いことに、reversedは関数ではありませんが、sortedは関数です。

REPLセッションを開き、help(reversed)と入力します:

class reversed(object)
 |  reversed(sequence) -> reverse iterator over values of the sequence
 |  
 |  Return a reverse iterator

これは実際に、逆反復子を返すために使用されるクラスです。

では、reversedは関数ではありません。しかし、なぜですか?

これは答えるのが少し難しいです。 1つの説明は、反復子が遅延評価を行うことです。これには、ある時点でのイテレータの現在の状態に関する情報を格納するための何らかのコンテナが必要です。これはオブジェクト、つまりclassを介して行うのが最適です。

4
cs95