Pythonの itertools.repeat()
クラスについて考えることができるすべての用途で、同じ効果を達成するための別の同等に(おそらくより多くの)許容可能なソリューションを考えることができます。例えば:
>>> [i for i in itertools.repeat('example', 5)]
['example', 'example', 'example', 'example', 'example']
>>> ['example'] * 5
['example', 'example', 'example', 'example', 'example']
>>> list(map(str.upper, itertools.repeat('example', 5)))
['EXAMPLE', 'EXAMPLE', 'EXAMPLE', 'EXAMPLE', 'EXAMPLE']
>>> ['example'.upper()] * 5
['EXAMPLE', 'EXAMPLE', 'EXAMPLE', 'EXAMPLE', 'EXAMPLE']
それが最も適切な解決策となるケースはありますか?もしそうなら、どのような状況で?
itertools.repeat
関数は怠惰です。 1つのアイテムに必要なメモリのみを使用します。一方、(a,) * n
および[a] * n
イディオムは、メモリ内にオブジェクトのn個のコピーを作成します。 5つの項目の場合、乗算のイディオムの方がおそらく優れていますが、たとえば100万回繰り返す必要がある場合は、リソースの問題に気付く可能性があります。
それでも、itertools.repeat
の多くのstaticの使用を想像するのは難しいです。ただし、itertools.repeat
がfunctionであるという事実により、多くの機能アプリケーションで使用できます。たとえば、反復可能な入力を操作するライブラリ関数func
があるとします。場合によっては、さまざまなアイテムのリストが事前に作成されていることがあります。また、統一されたリストを操作したい場合もあります。リストが大きい場合、itertools.repeat
はメモリを節約します。
最後に、repeat
は、itertools
ドキュメントで説明されているいわゆる「イテレータ代数」を可能にします。 itertools
モジュール自体でさえrepeat
関数を使用します。たとえば、次のコードはitertools.izip_longest
の同等の実装として提供されています(実際のコードはおそらくCで記述されていますが)。下から7行repeat
を使用していることに注意してください。
class ZipExhausted(Exception):
pass
def izip_longest(*args, **kwds):
# izip_longest('ABCD', 'xy', fillvalue='-') --> Ax By C- D-
fillvalue = kwds.get('fillvalue')
counter = [len(args) - 1]
def sentinel():
if not counter[0]:
raise ZipExhausted
counter[0] -= 1
yield fillvalue
fillers = repeat(fillvalue)
iterators = [chain(it, sentinel(), fillers) for it in args]
try:
while iterators:
yield Tuple(map(next, iterators))
except ZipExhausted:
pass
itertools.repeatの主な目的は、mapまたはZipで使用される定数値のストリームを提供することです。
>>> list(map(pow, range(10), repeat(2))) # list of squares
[0, 1, 4, 9, 16, 25, 36, 49, 64, 81]
二次的な目的は、次のように固定回数ループする非常に高速な方法を提供することです。
for _ in itertools.repeat(None, 10000):
do_something()
これは次よりも高速です。
for i in range(10000):
do_something().
前者が勝つのは、既存のNoneオブジェクトの参照カウントを更新するだけだからです。 range()またはxrange()は、10,000個の異なる整数オブジェクトを製造する必要があるため、後者は失われます。
Guido自身が timeit() モジュールでその高速ループ手法を使用していることに注意してください。 https://hg.python.org/cpython/file/2.7/Lib/timeit.py#l195 のソースを参照してください:
if itertools:
it = itertools.repeat(None, number)
else:
it = [None] * number
gcold = gc.isenabled()
gc.disable()
try:
timing = self.inner(it, self.timer)
_foo * 5
_の例は、表面的にはitertools.repeat(foo, 5)
に似ていますが、実際にはまったく異なります。
_foo * 100000
_と書く場合、インタプリタは答えを出す前にfoo
の100,000コピーを作成する必要があります。したがって、これは非常にコストがかかり、メモリに優しい操作です。
しかし、itertools.repeat(foo, 100000)
と書くと、インタプリタは同じ関数を提供するiteratorを返すことができ、計算する必要はありません。必要になるまで結果を取得します。たとえば、シーケンス内の各結果を知りたい関数で使用します。
これがイテレータの主な利点です。イテレータは、本当に答えが必要になるまで、リストの一部(またはすべて)の計算を延期することができます。
イテレータです。ここでの大きな手がかり:それはitertoolsモジュールにあります。リンクしたドキュメントから:
itertools.repeat(object [、times])オブジェクトを返すiteratorを作成し、もう一度。 times引数が指定されていない限り、無期限に実行されます。
そのため、これらすべてのものがメモリに保存されることはありません。あなたがそれを使いたい例は
n = 25
t = 0
for x in itertools.repeat(4):
if t > n:
print t
else:
t += x
これにより、任意の数の4
s、または無限のリストが必要になる可能性のあるもの。
前に述べたように、それはZip
でうまく機能します:
もう一つの例:
_from itertools import repeat
fruits = ['apples', 'oranges', 'bananas']
# Initialize inventory to zero for each fruit type.
inventory = dict( Zip(fruits, repeat(0)) )
_
結果:
_{'apples': 0, 'oranges': 0, 'bananas': 0}
_
これを繰り返さずに行うには、len(fruits)
を使用する必要があります。
私は通常、チェーンとサイクルと組み合わせてリピートを使用します。次に例を示します。
from itertools import chain,repeat,cycle
fruits = ['apples', 'oranges', 'bananas', 'pineapples','grapes',"berries"]
inventory = list(Zip(fruits, chain(repeat(10,2),cycle(range(1,3)))))
print inventory
最初の2つの果物を値10として配置し、残りの果物の値1と2を循環させます。