考虑以下Python代码:
b = [1,2,3,4,5,6,7]
a = iter(b)
for x in a :
if (x % 2) == 0 :
print(next(a))
将打印3、5和7。是在可靠构造上迭代的变量上使用next
(您可以假定StopIteration异常无关紧要或将被处理),或者在循环内部循环遍历的迭代器的修改是否违反某些原理?
答案 0 :(得分:24)
在协议方面或理论上,没有什么错误阻止您编写此类代码。耗尽的迭代器it
会在随后的对StopIteration
的每次调用中抛出it.__next__
,因此,如果您用{{1} } / for
在循环主体中调用。
我建议不要编写此类代码,因为该程序很难推理。如果情况变得比您在此处显示的要复杂,至少我将不得不用笔和纸进行一些输入,然后确定正在发生的事情。
实际上,假设您要打印每个以偶数开头的数字,您的代码段甚至可能不会像您认为的那样工作。
next
为什么__next__
会被跳过,尽管它前面带有偶数>>> b = [1, 2, 4, 7, 8]
>>> a = iter(b)
>>> for x in a:
...: if x%2 == 0:
...: print(next(a, 'stop'))
4
stop
?
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
循环分配,因为在x = 4
循环有机会查看之前,显式for
调用从迭代器中弹出了该元素再次迭代。
那是我讨厌弄清楚阅读代码时的细节。
如果要对“ next
”对中的可迭代对象(包括迭代器)进行迭代,请使用for
文档中的(element, next_element)
recipe。
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)
及其配方为编写可读的与迭代相关的代码提供了许多强大的抽象。在more_itertools
模块中可以找到更有用的帮助程序,包括pairwise
的实现。
答案 1 :(得分:4)
这取决于您所说的“安全”的含义,正如其他人所评论的那样,这还可以,但是您可以想象一些人为的情况可能会让您失望,例如,请考虑以下代码段:
b = [1,2,3,4,5,6,7]
a = iter(b)
def yield_stuff():
for item in a:
print(item)
print(next(a))
yield 1
list(yield_stuff())
在Python <= 3.6上,它运行并输出:
1
2
3
4
5
6
7
但是在Python 3.7上它会引发RuntimeError: generator raised StopIteration
。当然,如果您阅读PEP 479,并且无论如何都想处理StopIteration
,您可能永远都不会遇到它,这是可以预期的,但是我想在{{内调用next()
的用例1}}循环很少见,通常有更清晰的代码重构方法。
答案 2 :(得分:4)
如果您修改代码,以查看迭代器.strftime()
会发生什么:
a
您将看到打印输出:
b = [1,2,3,4,5,6,7]
a = iter(b)
for x in a :
print 'x', x
if (x % 2) == 0 :
print 'a', next(a)
这意味着当您执行x 1
x 2
a 3
x 4
a 5
x 6
a 7
时,您正在前进迭代器。如果您需要(或将来需要)使用迭代器 a 进行其他操作,则将遇到问题。为了完全安全,请使用itertools模块中的各种配方。例如:
next(a)
并不是说,您可以在循环的任何位置完全控制当前迭代器元素或下一迭代器元素。