31

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()最も適切な解決策になるケースはありますか? もしそうなら、どのような状況で?

4

6 に答える 6

37

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)
于 2012-02-01T15:51:04.080 に答える
32

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

それでも、 の多くの静的用途を想像するのは困難ですitertools.repeat。ただし、これitertools.repeat関数であるため、多くの関数型アプリケーションで使用できます。たとえばfunc、反復可能な入力を操作するライブラリ関数があるとします。場合によっては、さまざまなアイテムのリストが事前に作成されていることがあります。また、均一なリストを操作したい場合もあります。リストが大きい場合は、itertools.repeatメモリを節約できます。

最後にrepeat、ドキュメントで説明されているいわゆる「イテレータ代数」を可能にしitertoolsます。itertoolsモジュール自体でさえrepeat関数を使用します。たとえば、次のコードはitertools.izip_longest(実際のコードはおそらく C で書かれていますが) と同等の実装として与えられています。repeat下から 7 行を使用していることに注意してください。

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
于 2012-01-30T04:14:57.450 に答える
16

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

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

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

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

于 2012-01-30T04:08:59.597 に答える
4

イテレータです。ここに大きな手がかりがあります。それは itertools モジュールにあります。リンク先のドキュメントから:

itertools.repeat (object[, times]) オブジェクトを何度も返すイテレータを作成します。times 引数が指定されない限り、無期限に実行されます。

そのため、すべてをメモリに保持することはできません。使用したい例は次のとおりです

n = 25
t = 0
for x in itertools.repeat(4):
    if t > n:
        print t
    else:
        t += x

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

于 2012-01-30T04:09:23.087 に答える
2

前述のように、次の場合にうまく機能し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)ます。

于 2013-11-27T08:34:32.127 に答える