方法:
# On Python 2 only, get zip that lazily generates results instead of returning list
from future_builtins import zip
from collections import deque
from itertools import count
def ilen(it):
# Make a stateful counting iterator
cnt = count()
# zip it with the input iterator, then drain until input exhausted at C level
deque(zip(it, cnt), 0) # cnt must be second zip arg to avoid advancing too far
# Since count 0 based, the next value is the count
return next(cnt)
(deque
、count
とzip
が全てCで実装されている)、それはCPythonの上のCコードのループを実行len(list(it))
等。通常、ループごとのバイトコード実行を避けることは、CPythonのパフォーマンスの鍵です。
それは(__length_hint__
を提供していない任意の入力イテラブルのために利用可能である可能性が高いではない__length_hint__
を使用してlist
詐欺、itertools
機能は、多くの場合、仕事の特別な動作モードを持っている性能を比較するための公正なテストケースを思い付くために意外に難しいです次の値が要求される前に各ループで返された値が解放されると、より速くなります。deque
はmaxlen=0
となります)。私が使用したテストケースは、Python 3.3のyield from
を使用して、入力を取り、特別itertools
リターン容器最適化または__length_hint__
を欠いCレベル発生を返すジェネレータ関数を作成することであった。
def no_opt_iter(it):
yield from it
そしてipython
%timeit
を使用してマジックは(100のために異なる定数を代入):
>>> %%timeit -r5 fakeinput = (0,) * 100
... ilen(no_opt_iter(fakeinput))
入力がlen(list(it))
は、Python 3.5のx64を実行しているLinuxボックス上で、メモリの問題を引き起こすことに十分な大きさではない、私の解決策は、長い股関節を約50%になりますn def ilen(it): return len(list(it))
、入力の長さに関係なく。入力の最小のために
、セットアップ費用はdeque
/zip
/count
/next
を呼び出すためには、それが33%である長さ0の入力のための私のマシン上def ilen(it): sum(1 for x in it)
(約200ナノ秒以上無限に長いこの方法をとる意味単純なsum
のアプローチよりも増加します)。しかし、より長い入力の場合は、追加の要素あたり約半分の時間で実行されます。長さ5の入力の場合、コストは同等であり、長さ50-100の範囲のどこかで、実際の作業に比べて初期のオーバーヘッドは目立たない。 sum
のアプローチには約2倍の時間がかかります。
基本的には、メモリ使用の問題や入力が制限されたサイズでなく、スピードを重視する場合は、このソリューションを使用してください。入力が制限されて小さくても、len(list(it))
がおそらく最も良いでしょう。制限されていないが簡潔さ/簡潔さの場合は、sum(1 for x in it)
を使用します。
(2)で_' 'と衝突し、変数名として' _'使用しないでくださいインタラクティブなインタプリタと(3)共通のgettextエイリアスと衝突します。 –
@Sven:私は未使用の変数に対して常に '_'を使います(PrologとHaskellプログラミングの習慣)。 (1)これを最初に尋ねる理由です。私は(2)と(3)を考慮しなかった、それらを指摘してくれてありがとう! –
duplicated:http://stackoverflow.com/questions/390852/is-there-any-built-in-way-to-get-the-length-of-an-iterable-in-python – tokland