我可以为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']
在任何情况下,它是最合适的解决方案吗?如果是这样,在什么情况下?
答案 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循环。