如何限制循环的迭代次数?

新手上路,请多包涵

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

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

天真的实现

来自其他语言的 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

更惯用的实现

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

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

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

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

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

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

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

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

第二个问题:为什么不将其内置到枚举中?

原文由 Russia Must Remove Putin 发布,翻译遵循 CC BY-SA 4.0 许可协议

阅读 1k
2 个回答

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

>  for index, item in enumerate(items):
>     print(item)
>     if index == limit:
>         break
>
> ```
>
> 有没有一种更短的、惯用的方式来写上面的内容?如何?

## 包括索引

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

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

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

## `zip(range(limit), items)`

使用 Python 3, `zip` 和 `range` 返回 iterables,它通过管道传输数据而不是在列表中为中间步骤具体化数据。

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


To get the same behavior in Python 2, just substitute `xrange` for `range` and `itertools.izip` for `zip` .

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 指出的那样,我们还可以使用 enumerate 组合 islice。

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


> # 为什么不将其内置到 `enumerate` ?

这是用纯 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


对于那些已经使用 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)


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

至于 _为什么_ enumerate 没有停止参数,这是最初提出的(参见 [PEP 279](https://www.python.org/dev/peps/pep-0279/) ):

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

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

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

另一个答案说:

> 为什么不简单地使用
>
> ```
> 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): …

”`

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

原文由 Russia Must Remove Putin 发布,翻译遵循 CC BY-SA 4.0 许可协议

您可以为此使用 itertools.islice 。 It accepts start , stop and step arguments, if you’re passing only one argument then it is considered as stop .它适用于任何可迭代对象。

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

演示:

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

来自文档的示例:

 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

原文由 Ashwini Chaudhary 发布,翻译遵循 CC BY-SA 3.0 许可协议

撰写回答
你尚未登录,登录后可以
  • 和开发者交流问题的细节
  • 关注并接收问题和回答的更新提醒
  • 参与内容的编辑和改进,让解决方法与时俱进
推荐问题