在Python中的for循环中使用next是否安全?

Jac*_*ley 24 python

请考虑以下Python代码:

b = [1,2,3,4,5,6,7]
a = iter(b)
for x in a :
    if (x % 2) == 0 :
        print(next(a))
Run Code Online (Sandbox Code Playgroud)

这将打印3,5和7.是否在next可靠的构造上迭代变量的使用(您可以假设StopIteration异常不是问题或将被处理),或者迭代器的修改是否循环在循环内部构成违反某些原则?

tim*_*geb 24

在协议方面或理论上没有任何错误会阻止您编写此类代码.耗尽的迭代器it将抛出StopIteration每个后续调用it.__next__,因此for如果你在循环体中用next/ __next__call 耗尽迭代器,那么循环技术上不会介意.

我建议不要编写这样的代码,因为程序很难推理.如果场景比你在这里展示的要复杂得多,那么至少我需要通过笔和纸进行一些输入并找出正在发生的事情.

实际上,假设您要打印前面带偶数的每个数字,您的代码段甚至可能不会像您认为的那样表现.

>>> b = [1, 2, 4, 7, 8]                                              
>>> a = iter(b)                                                      
>>> for x in a: 
...:    if x%2 == 0: 
...:        print(next(a, 'stop'))                                   
4
stop
Run Code Online (Sandbox Code Playgroud)

为什么7跳过虽然前面是偶数4

>>>> a = iter(b)                                                      
>>>> for x in a: 
...:     print('for loop assigned x={}'.format(x)) 
...:     if x%2 == 0: 
...:         nxt = next(a, 'stop') 
...:         print('if popped nxt={} from iterator'.format(nxt)) 
...:         print(nxt)
...:                                               
for loop assigned x=1
for loop assigned x=2
if popped nxt=4 from iterator
4
for loop assigned x=7
for loop assigned x=8
if popped nxt=stop from iterator
stop
Run Code Online (Sandbox Code Playgroud)

结果x = 4从未被for循环赋值,因为显式next调用在for循环有机会再次查看迭代器之前从迭代器中弹出该元素.

这是我讨厌阅读代码时的细节.


如果你想在"遍历可迭代(包括迭代器)(element, next_element)"对,使用pairwise 配方itertools文档.

from itertools import tee                                         

def pairwise(iterable):
    "s -> (s0,s1), (s1,s2), (s2, s3), ..." 
    a, b = tee(iterable) 
    next(b, None) 
    return zip(a, b) 
Run Code Online (Sandbox Code Playgroud)

演示:

>>> b = [1,2,3,4,5,6,7]                                               
>>> a = iter(b)                                                       
>>>                                                                   
>>> for x, nxt in pairwise(a): # pairwise(b) also works 
...:    print(x, nxt)                                                                      
1 2
2 3
3 4
4 5
5 6
6 7
Run Code Online (Sandbox Code Playgroud)

通常,itertools与其配方一起提供了许多强大的抽象,用于编写可读的迭代相关代码.在more_itertools模块中可以找到更有用的帮助程序,包括实现pairwise.

  • @VincentSavard我认为Jack意味着你不能将迭代器切割成打开文件所得到的行.来自[`itertools` docs](https://docs.python.org/3/library/itertools.html#itertools-recipes)的`pairwise`处理它. (5认同)