我可以在for循环体中重新定义迭代器变量吗?

时间:2016-04-09 22:13:50

标签: python-2.7 for-loop scope iterator

这是一个人为的例子:

from itertools import cycle
def foobar():
    A = [1,2,3,4,5]
    B = [6,7,8,9,10]
    it = cycle(A)
    tmp=0
    for item in it:
        print item, tmp
        if tmp>10:
            it=cycle(B) #is this possible?
        if tmp>30:
            return tmp
        tmp+=item

输出:

1 0
2 1
3 3
4 6
5 10
1 15
2 16
3 18
4 21
5 25
1 30
2 31

我试图在for循环中迭代一个iterable直到满足条件,此时我重新定义 it 以便它使用相同的循环体迭代另一个列表

代码无法以我当前的形式运行:它继续使用循环(A),即使我重新定义了 it = cycle( B)。我想有一些范围问题,循环内的代码无法修改循环条件。有没有办法完成我在这里尝试做的事情,或者我需要制作重复或嵌套的for循环来迭代多个列表?

2 个答案:

答案 0 :(得分:1)

没有办法直接做你想做的事。代码中的for循环仅引用首次启动时传递的可迭代的初始值。它在该iterable上调用iter()并使用它获取的迭代器来获取迭代的项。重新绑定在for中使用的变量不会改变任何内容,因为在获得迭代器之后,循环的内部逻辑不会使用该变量。

我怀疑使用break,另一个循环将是解决此问题的最佳方法。但是如果确实希望保持循环代码尽可能接近你当前的代码,那么你可以编写自己的迭代器,它允许从一个(内部存储的)迭代器交换到另一个迭代器:

class swapable_iterator(object):
    def __init__(self, it):
        self.it = it

    def __iter__(self):
        return self

    def next(self):
        return next(self.it)

    __next__ = next # Python 3 compatibility

    def swap(self, new_it):
        self.it = new_it

你仍然需要稍微改变你的循环逻辑,因为它目前会一遍又一遍地交换cycle(B)tmp > 10之后),只看到第一个值。

尝试类似:

def foobar():
    A = [1,2,3,4,5]
    B = [6,7,8,9,10]
    it = swapable_iterator(cycle(A)) # wrap the cycle with our custom iterator object
    tmp=0
    for item in it:
        print item, tmp
        if item == 5 and tmp > 10: # this will happen exactly once with the A and B above
            it.swap(cycle(B)) # this modifies "it" in place, rather than rebinding it
        if tmp>30:
            return tmp
        tmp+=item

答案 1 :(得分:0)

我认为不可能改变for循环的条件。您总是可以使用while和some语句来更改要使用的迭代器的下一项,例如:

from itertools import cycle

def foobar():
    A = [1, 2, 3, 4, 5]
    B = [6, 7, 8, 9, 10]
    tmp = 0
    iter_A = cycle(A)
    iter_B = cycle(B)
    use_A = True
    while True:
        if use_A:
            item = next(iter_A)
        else:
            item = next(iter_B)
        print item, tmp
        if tmp > 10:
            use_A = False
        if tmp > 30:
            return tmp

        tmp += item

输出:

1 0
2 1
3 3
4 6
5 10
1 15
6 16
7 22
8 29
9 37

我希望这会有所帮助。

<强>更新

根据Martijn Pieters的评论,以下内容也应该有效:

from itertools import cycle

def foobar():
    A = [1, 2, 3, 4, 5]
    B = [6, 7, 8, 9, 10]
    tmp = 0
    iter_A = cycle(A)
    iter_B = cycle(B)
    it = iter_A
    while True:
        item = next(it)
        print item, tmp
        if tmp > 10:
            it = iter_B
        if tmp > 30:
            return tmp

        tmp += item