在Python中的for循环中使用next安全吗?
考虑以下Python代码:在Python中的for循环中使用next安全吗?,python,Python,考虑以下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异常不是问题或将被处理),或者对循环中循环的迭代器的修改是否违反了某些原则?从协议角度或理论上讲,没有什么错误会阻止您编写此类代码。一个耗尽的迭代器it将在以后每次调用it时抛出StopIter
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异常不是问题或将被处理),或者对循环中循环的迭代器的修改是否违反了某些原则?从协议角度或理论上讲,没有什么错误会阻止您编写此类代码。一个耗尽的迭代器it
将在以后每次调用it时抛出StopIteration
。\uuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuu
我建议不要编写这样的代码,因为程序很难推理。如果这个场景比你在这里展示的要复杂一点,至少我需要用笔和纸来完成一些输入,并计算出发生了什么
事实上,您的代码片段的行为甚至可能与您认为的不一样,假设您希望打印前面有偶数的每个数字
>>> 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
循环有机会再次查看迭代器之前从迭代器中弹出了该元素
这是我在阅读代码时不愿透露的细节
如果要对“(元素,下一个元素)
”对中的一个iterable(包括迭代器)进行迭代,请使用itertools
文档中的pairwise
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)
演示:
通常,itertools
及其配方为编写可读的迭代相关代码提供了许多强大的抽象。更有用的助手可以在模块中找到,包括./p>< p>的实现。它取决于你所说的“安全”的意思,正如其他人所评论的,它是可以的,但是你可以想象一些可能会吸引你的人为的情况,例如,考虑这个代码片段:
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上如果您修改代码以查看迭代器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
这意味着当您执行next(a)
时,您正在向前移动迭代器。如果您需要(或将来需要)使用迭代器a执行其他操作,您将遇到问题。为了完全安全,请使用模块中的各种配方。例如:
from itertools import tee, izip
def pairwise(iterable):
"s -> (s0,s1), (s1,s2), (s2, s3), ..."
a, b = tee(iterable)
next(b, None)
return izip(a, b)
b = [1,2,3,4,5,6,7]
a = iter(b)
c = pairwise(a)
for x, next_x in c:
if x % 2 == 0:
print next_x
这里并不是说你可以完全控制当前迭代器元素或下一个迭代器元素的任何位置。一个需要思考的问题:如果你跳过if
条件并总是调用next(A)
,那么会发生什么?只要你知道你在做什么就可以了。好的,但如果其他人正在使用/阅读代码,则值得一提。除了学术兴趣之外,我无法理解为什么要编写这样的代码?@copper.hat您可以在这里看到——作为激励示例。其目的是在文件中找到一行太大而无法读入内存的内容,然后处理下一行。timgeb答案中的pairwise
recipe是一种更清晰的方法。当然,上面的例子是一个玩具,你可以在这里看到一个激励性的例子:提问者希望搜索一行中的特定序列,然后打印下一行。@JackAidley通常,在这种情况下,我相信,列表本身被一个元素抵消后的zip
模式很有趣。例如,您可以执行:对于上一个、当前的压缩包(my_list,my_list[1:]):
。这可以防止您使用next
玩花哨的把戏,而且可读性和优雅性都很好。@VincentSavard这在从文件中读取行时不起作用。@JackAidley为什么不起作用?您可以读取前两行来创建第一个元素,然后只需逐行读取文件即可。@VincentSavard我认为Jack的意思是,您无法在打开文件时获得的行上切片迭代器<代码>成对
从控制柄可以看出。很好地捕捉到了版本之间的细微差异。
x 1
x 2
a 3
x 4
a 5
x 6
a 7
from itertools import tee, izip
def pairwise(iterable):
"s -> (s0,s1), (s1,s2), (s2, s3), ..."
a, b = tee(iterable)
next(b, None)
return izip(a, b)
b = [1,2,3,4,5,6,7]
a = iter(b)
c = pairwise(a)
for x, next_x in c:
if x % 2 == 0:
print next_x