31

对于我能想到的 Pythonitertools.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的主要目的是提供用于mapzip的常量值流:

>>> 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函数是惰性的;它只使用一项所需的内存。另一方面,(a,) * n[a] * n成语在内存中创建对象的 n 个副本。对于五个项目,乘法习语可能更好,但如果你必须重复某件事,比如一百万次,你可能会注意到资源问题。

尽管如此,还是很难想象. 但是,它是一个函数itertools.repeat这一事实允许您在许多函数式应用程序中使用它。例如,您可能有一些对可迭代输入进行操作的库函数。有时,您可能已经预先构建了各种项目的列表。其他时候,您可能只想对统一列表进行操作。如果列表很大,将节省您的内存。itertools.repeatfuncitertools.repeat

最后,使文档repeat中描述的所谓“迭代器代数”成为可能。itertools甚至itertools模块本身也使用该repeat功能。例如,下面的代码给出了一个等效的实现itertools.izip_longest(即使真正的代码可能是用 C 编写的)。注意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
于 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 回答
0

我通常将重复与链和循环结合使用。这是一个例子:

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。

于 2017-02-14T00:37:46.303 回答