Python itertools.repeat的目的是什么?

时间:2012-01-30 03:57:20

标签: python python-3.x itertools

我可以为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']

在任何情况下,它是最合适的解决方案吗?如果是这样,在什么情况下?

6 个答案:

答案 0 :(得分:19)

itertools.repeat函数是懒惰的;它只使用一个项目所需的内存。另一方面,(a,) * n[a] * n成语在内存中创建n个对象副本。对于五个项目,乘法习惯可能更好,但如果你不得不重复一些事情,比如一百万次,你可能会注意到资源问题。

但是,很难想象许多静态用于itertools.repeat。但是,itertools.repeat函数的事实允许您在许多功能应用程序中使用它。例如,您可能有一些库函数func,它在可迭代的输入上运行。有时,您可能预先构建了各种项目的列表。其他时候,您可能只想在统一列表上操作。如果列表很大,itertools.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

答案 1 :(得分:18)

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)

答案 2 :(得分:14)

foo * 5的示例看起来与itertools.repeat(foo, 5)非常相似,但实际上却完全不同。

如果你写foo * 100000,解释者必须先创建100,000份foo,然后才能给你答案。因此,这是一种非常昂贵且对记忆不友好的操作。

但是如果你写itertools.repeat(foo, 100000),解释器可以返回一个提供相同功能的迭代器,并且在你需要它之​​前不需要计算结果 - 比如说在想要知道序列中每个结果的函数中使用它。

这是迭代器的主要优点:它们可以推迟列表的一部分(或全部)的计算,直到你真的需要答案为止。

答案 3 :(得分:2)

这是一个迭代器。这里有一个大线索:它在itertools模块中。从您链接到的文档:

  

itertools.repeat(object [,times])   创建一个一遍又一遍地返回对象的 迭代器 。除非指定了times参数,否则无限期运行。

所以你永远不会在记忆中拥有所有这些东西。您想要使用它的示例可能是

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

因为这将允许您任意数量的4,或者您可能需要的无限列表。

答案 4 :(得分: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)

答案 5 :(得分: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循环。