reversed
のタイプは「タイプ」です:
>>> type(reversed)
<class 'type'>
sorted
のタイプは「組み込み関数またはメソッド」です:
>>> type(sorted)
<class 'builtin_function_or_method'>
ただし、実際には同じように見えます。機能の明らかな違い(逆順と並べ替えの順序)を除いて、この実装の違いの理由は何ですか?
違いは、reversed
はイテレータ(遅延評価でもある)であり、sorted
は「熱心に」機能する関数であることです。
map
、Zip
、filter
、reversed
、...のようなすべての組み込みイテレータ(少なくともpython-3.xでは)は次のように実装されますクラス。熱心に動作する組み込み関数は関数ですが、 min
、max
、any
、all
および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つあります。それらを連鎖させると、メモリ効率が非常に高くなります。明示的に「要求」されない限り、中間リストを作成する必要はありません。これが、map
、Zip
、および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>
reversed
とsorted
の違いは何ですか?
興味深いことに、reversed
は関数ではありませんが、sorted
は関数です。
REPLセッションを開き、help(reversed)
と入力します:
class reversed(object)
| reversed(sequence) -> reverse iterator over values of the sequence
|
| Return a reverse iterator
これは実際に、逆反復子を返すために使用されるクラスです。
では、
reversed
は関数ではありません。しかし、なぜですか?
これは答えるのが少し難しいです。 1つの説明は、反復子が遅延評価を行うことです。これには、ある時点でのイテレータの現在の状態に関する情報を格納するための何らかのコンテナが必要です。これはオブジェクト、つまりclass
を介して行うのが最適です。