如何限制 Python 中循环的迭代?

声明:本页面是StackOverFlow热门问题的中英对照翻译,遵循CC BY-SA 4.0协议,如果您需要使用它,必须同样遵循CC BY-SA许可,注明原文地址和作者信息,同时你必须将它归于原作者(不是我):StackOverFlow 原文地址: http://stackoverflow.com/questions/36106712/
Warning: these are provided under cc-by-sa 4.0 license. You are free to use/share it, But you must attribute it to the original authors (not me): StackOverFlow

提示:将鼠标放在中文语句上可以显示对应的英文。显示中英文
时间:2020-08-19 17:24:54  来源:igfitidea点击:

How can I limit iterations of a loop in Python?

pythonenumerate

提问by Aaron Hall

Say I have a list of items, and I want to iterate over the first few of it:

假设我有一个项目列表,我想迭代其中的前几个:

items = list(range(10)) # I mean this to represent any kind of iterable.
limit = 5

Naive implementation

幼稚的实现

The Python na?f coming from other languages would probably write this perfectly serviceable and performant (if unidiomatic) code:

来自其他语言的 Python na?f 可能会编写这个非常有用和高性能(如果单语)的代码:

index = 0
for item in items: # Python's `for` loop is a for-each.
    print(item)    # or whatever function of that item.
    index += 1
    if index == limit:
        break

More idiomatic implementation

更惯用的实现

But Python has enumerate, which subsumes about half of that code nicely:

但是 Python 有 enumerate,它很好地包含了大约一半的代码:

for index, item in enumerate(items):
    print(item)
    if index == limit: # There's gotta be a better way.
        break

So we've about cut the extra code in half. But there's gotta be a better way.

所以我们将额外的代码减半。但一定有更好的方法。

Can we approximate the below pseudocode behavior?

我们可以近似下面的伪代码行为吗?

If enumerate took another optional stopargument (for example, it takes a startargument like this: enumerate(items, start=1)) that would, I think, be ideal, but the below doesn't exist (see the documentation on enumerate here):

如果 enumerate 采用另一个可选stop参数(例如,它采用这样的start参数enumerate(items, start=1):),我认为这是理想的,但以下不存在(请参阅此处的 enumerate 文档):

# hypothetical code, not implemented:
for _, item in enumerate(items, start=0, stop=limit): # `stop` not implemented
    print(item)

Note that there would be no need to name the indexbecause there is no need to reference it.

请注意,不需要命名 ,index因为不需要引用它。

Is there an idiomatic way to write the above? How?

有没有一种惯用的方式来写上面的内容?如何?

A secondary question: why isn't this built into enumerate?

第二个问题:为什么这不内置在枚举中?

回答by Aaron Hall

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?

如何限制 Python 中循环的迭代?

for index, item in enumerate(items):
    print(item)
    if index == limit:
        break

有没有更短的、惯用的方式来写上面的内容?如何?

Including the index

包括索引

zipstops on the shortest iterable of its arguments. (In contrast with the behavior of zip_longest, which uses the longest iterable.)

zip在其参数的最短迭代上停止。(与 的行为相反zip_longest,它使用最长的可迭代对象。)

rangecan provide a limited iterable that we can pass to zip along with our primary iterable.

range可以提供一个有限的迭代器,我们可以将它与我们的主要迭代器一起传递给 zip。

So we can pass a rangeobject (with its stopargument) to zipand use it like a limited enumerate.

因此,我们可以将range对象(及其stop参数)传递给zip并像有限枚举一样使用它。

zip(range(limit), items)

zip(range(limit), items)

Using Python 3, zipand rangereturn iterables, which pipeline the data instead of materializing the data in lists for intermediate steps.

使用 Python 3,ziprange返回可迭代对象,它对数据进行管道传输,而不是将列表中的数据具体化以用于中间步骤。

for index, item in zip(range(limit), items):
    print(index, item)

To get the same behavior in Python 2, just substitute xrangefor rangeand itertools.izipfor zip.

为了获得在Python 2相同的行为,刚刚替补xrangerangeitertools.izipzip

from itertools import izip
for index, item in izip(xrange(limit), items):
    print(item)

If not requiring the index, itertools.islice

如果不需要索引, itertools.islice

You can use itertools.islice:

您可以使用itertools.islice

for item in itertools.islice(items, 0, stop):
    print(item)

which doesn't require assigning to the index.

这不需要分配给索引。

Composing enumerate(islice(items, stop))to get the index

编写enumerate(islice(items, stop))以获取索引

As Pablo Ruiz Ruiz points out, we can also compose islice with enumerate.

正如 Pablo Ruiz Ruiz 所指出的,我们也可以用 enumerate 组合 islice。

for index, item in enumerate(islice(items, limit)):
    print(index, item)

Why isn't this built into enumerate?

为什么这不是内置的enumerate

Here's enumerate implemented in pure Python (with possible modifications to get the desired behavior in comments):

这是在纯 Python 中实现的枚举(可能进行修改以在注释中获得所需的行为):

def enumerate(collection, start=0):  # could add stop=None
    i = start
    it = iter(collection)
    while 1:                         # could modify to `while i != stop:`
        yield (i, next(it))
        i += 1

The above would be less performant for those using enumerate already, because it would have to check whether it is time to stop every iteration. We can just check and use the old enumerate if don't get a stop argument:

对于那些已经使用 enumerate 的人来说,上面的性能会降低,因为它必须检查是否是时候停止每次迭代了。如果没有停止参数,我们可以检查并使用旧的枚举:

_enumerate = enumerate

def enumerate(collection, start=0, stop=None):
    if stop is not None:
        return zip(range(start, stop), collection)
    return _enumerate(collection, start)

This extra check would have a slight negligible performance impact.

这种额外的检查对性能的影响可以忽略不计。

As to whyenumerate does not have a stop argument, this was originally proposed (see PEP 279):

至于为什么enumerate 没有停止参数,这是最初提出的(参见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

这个函数最初是用可选的开始和停止参数提出的。GvR [Guido van Rossum] 指出函数调用 enumerate(seqn, 4, 6)有一个替代的、合理的解释,作为一个切片将返回序列的第四和第五个元素。为了避免歧义,可选参数被删除,即使这意味着失去作为循环计数器的灵活性。这种灵活性对于从一开始计数的常见情况最为重要,例如:

for linenum, line in enumerate(source,1):  print linenum, line

So apparently startwas kept because it was very valuable, and stopwas dropped because it had fewer use-cases and contributed to confusion on the usage of the new function.

显然start,保留它是因为它非常有价值,而stop被删除是因为它的用例较少,并且导致了对新功能使用的混淆。

Avoid slicing with subscript notation

避免使用下标符号进行切片

Another answer says:

另一个答案说:

Why not simply use

for item in items[:limit]: # or limit+1, depends

为什么不简单地使用

for item in items[:limit]: # or limit+1, depends

Here's a few downsides:

这里有一些缺点:

  • It only works for iterables that accept slicing, thus it is more limited.
  • If they do accept slicing, it usually creates a new data structure in memory, instead of iterating over the reference data structure, thus it wastes memory (All builtin objects make copies when sliced, but, for example, numpy arrays make a view when sliced).
  • Unsliceable iterables would require the other kind of handling. If you switch to a lazy evaluation model, you'll have to change the code with slicing as well.
  • 它只适用于接受切片的迭代,因此它更受限制。
  • 如果他们确实接受切片,它通常会在内存中创建一个新的数据结构,而不是遍历引用数据结构,因此会浪费内存(所有内置对象在切片时都会进行复制,但是,例如,numpy 数组在切片时会生成视图)。
  • 不可切片的可迭代对象需要其他类型的处理。如果您切换到惰性求值模型,则还必须使用切片来更改代码。

You should only use slicing with subscript notation when you understand the limitations and whether it makes a copy or a view.

当您了解限制以及它是制作副本还是视图时,您应该只使用带有下标符号的切片。

Conclusion

结论

I would presume that now the Python community knows the usage of enumerate, the confusion costs would be outweighed by the value of the argument.

我认为现在 Python 社区知道 enumerate 的用法,混淆成本将被参数的价值所抵消。

Until that time, you can use:

在此之前,您可以使用:

for index, element in zip(range(limit), items):
    ...

or

或者

for index, item in enumerate(islice(items, limit)):
    ...

or, if you don't need the index at all:

或者,如果您根本不需要索引:

for element in islice(items, 0, limit):
    ...

And avoid slicing with subscript notation, unless you understand the limitations.

并避免使用下标符号进行切片,除非您了解这些限制。

回答by Ashwini Chaudhary

You can use itertools.islicefor this. It accepts start, stopand steparguments, if you're passing only one argument then it is considered as stop. And it will work with any iterable.

您可以itertools.islice为此使用。它接受start,stopstep参数,如果你只传递一个参数,那么它被认为是stop。它适用于任何可迭代对象。

itertools.islice(iterable, stop)
itertools.islice(iterable, start, stop[, step])

Demo:

演示:

>>> from itertools import islice
>>> items = list(range(10))
>>> limit = 5
>>> for item in islice(items, limit):
    print item,
...
0 1 2 3 4

Example from docs:

来自文档的示例:

islice('ABCDEFG', 2) --> A B
islice('ABCDEFG', 2, 4) --> C D
islice('ABCDEFG', 2, None) --> C D E F G
islice('ABCDEFG', 0, None, 2) --> A C E G

回答by JeD

Why not simply use

为什么不简单地使用

for item in items[:limit]: # or limit+1, depends
    print(item)    # or whatever function of that item.

This will only work for some iterables, but since you specified Lists, it works.

这仅适用于某些可迭代对象,但由于您指定了列表,因此它有效。

It doesn't work if you use Sets or dicts etc.

如果您使用 Sets 或 dicts 等,则它不起作用。

回答by Pablo Ruiz Ruiz

Pass islice with the limit inside enumerate

使用枚举内的限制通过 islice

a = [2,3,4,2,1,4]

for a, v in enumerate(islice(a, 3)): 
   print(a, v)

Output:

输出:

0 2
1 3
2 4

回答by codeforester

Why not loop until the limit or the end of list, whichever occurs earlier, like this:

为什么不循环直到限制或列表末尾,以较早发生者为准,如下所示:

items = range(10)
limit = 5
for i in range(min(limit, len(items))):
  print items[i]

Output:

输出:

0
1
2
3
4

回答by Sami Al-Subhi

short solution

简短的解决方案

items = range(10)
limit = 5

for i in items[:limit]: print(i)