web-dev-qa-db-ja.com

Python itertools.repeatの目的は何ですか?

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']

それが最も適切な解決策となるケースはありますか?もしそうなら、どのような状況で?

26
Tyler Crompton

itertools.repeat関数は怠惰です。 1つのアイテムに必要なメモリのみを使用します。一方、(a,) * nおよび[a] * nイディオムは、メモリ内にオブジェクトのn個のコピーを作成します。 5つの項目の場合、乗算のイディオムの方がおそらく優れていますが、たとえば100万回繰り返す必要がある場合は、リソースの問題に気付く可能性があります。

それでも、itertools.repeatの多くのstaticの使用を想像するのは難しいです。ただし、itertools.repeatfunctionであるという事実により、多くの機能アプリケーションで使用できます。たとえば、反復可能な入力を操作するライブラリ関数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
23
HardlyKnowEm

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)
27

_foo * 5_の例は、表面的にはitertools.repeat(foo, 5)に似ていますが、実際にはまったく異なります。

_foo * 100000_と書く場合、インタプリタは答えを出す前にfooの100,000コピーを作成する必要があります。したがって、これは非常にコストがかかり、メモリに優しい操作です。

しかし、itertools.repeat(foo, 100000)と書くと、インタプリタは同じ関数を提供するiteratorを返すことができ、計算する必要はありません。必要になるまで結果を取得します。たとえば、シーケンス内の各結果を知りたい関数で使用します。

これがイテレータの主な利点です。イテレータは、本当に答えが必要になるまで、リストの一部(またはすべて)の計算を延期することができます。

15
John Feminella

イテレータです。ここでの大きな手がかり:それは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

これにより、任意の数の4s、または無限のリストが必要になる可能性のあるもの。

3

前に述べたように、それは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)を使用する必要があります。

2

私は通常、チェーンとサイクルと組み合わせてリピートを使用します。次に例を示します。

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を循環させます。

0