How can I limit iterations of a loop in Python?
for index, item in enumerate(items):
print(item)
if index == limit:
break
Is there a shorter, idiomatic way to write the above? How?
包括索引
zip
将停留在其参数中最短的可迭代对象。(与使用最长可迭代对象的 zip_longest
的行为相反。)
range
可以提供一个有限的可迭代对象,我们可以将其与主要的可迭代对象一起传递给 zip
。
因此,我们可以将一个 range
对象(带有其 stop
参数)传递给 zip
并像使用有限枚举器一样使用它。
zip(range(limit), items)
使用 Python 3,zip
和 range
返回可迭代对象,这些对象将数据流式传输,而不是将数据材料化为列表进行中间步骤。
for index, item in zip(range(limit), items):
print(index, item)
要在Python 2中获得相同的行为,只需将
range
替换为
xrange
,将
zip
替换为
itertools.izip
。
from itertools import izip
for index, item in izip(xrange(limit), items):
print(item)
如果不需要索引,可以使用itertools.islice
您可以使用itertools.islice
:
for item in itertools.islice(items, 0, stop):
print(item)
这不需要分配索引。
使用enumerate(islice(items, stop))
组合以获取索引
正如Pablo Ruiz Ruiz所指出的那样,我们也可以将islice与enumerate组合使用。
for index, item in enumerate(islice(items, limit)):
print(index, item)
为什么这不是内置于
enumerate
中的呢?
这里是纯Python实现的枚举(可能需要在注释中进行修改以获得所需行为):
def enumerate(collection, start=0):
i = start
it = iter(collection)
while 1:
yield (i, next(it))
i += 1
上面的代码对那些已经使用枚举的人来说性能会更低,因为它需要在每次迭代时检查是否到达停止时间。我们可以只进行检查并在没有停止参数的情况下使用旧的枚举。
_enumerate = enumerate
def enumerate(collection, start=0, stop=None):
if stop is not None:
return zip(range(start, stop), collection)
return _enumerate(collection, start)
这个额外的检查会对性能产生轻微可忽略的影响。
至于为什么枚举没有停止参数,这最初是提出来的(参见
PEP 279):
This function was originally proposed with optional start
and stop arguments. GvR [Guido van Rossum] pointed out that the function call
enumerate(seqn, 4, 6)
had an alternate, plausible interpretation as
a slice that would return the fourth and fifth elements of the
sequence. To avoid the ambiguity, the optional arguments were
dropped even though it meant losing flexibility as a loop counter.
That flexibility was most important for the common case of
counting from one, as in:
for linenum, line in enumerate(source,1): print linenum, line
显然,“start”被保留是因为它非常有价值,“stop”被删除是因为它的使用情况较少,会导致新函数的使用混乱。
避免使用下标符号进行切片
另一个答案说:
Why not simply use
for item in items[:limit]: # or limit+1, depends
以下是一些缺点:
- 它仅适用于可迭代对象,而且必须支持切片,因此它的使用范围更有限。
- 如果它们支持切片,通常会在内存中创建一个新的数据结构,而不是在引用数据结构上进行迭代,因此会浪费内存(所有内置对象在切片时都会复制,但例如numpy数组在切片时会创建一个视图)。
- 不可切片的可迭代对象需要另一种处理方式。如果您切换到惰性评估模型,则还必须更改带有切片代码的代码。
只有在您了解其限制以及它是复制还是视图时,才应使用下标符号进行切片。
结论
我认为现在Python社区知道了enumerate的用法,混淆成本将被价值所超越。
在那之前,您可以使用:
for index, element in zip(range(limit), items):
...
或者
for index, item in enumerate(islice(items, limit)):
...
或者,如果您根本不需要索引:
for element in islice(items, 0, limit):
...
避免使用下标符号进行切片,除非您了解其限制。
for element in islice(items, limit):
- Walter R