【问题标题】:Using the same iterator in nested for loops在嵌套的 for 循环中使用相同的迭代器
【发布时间】:2022-12-12 14:29:37
【问题描述】:

考虑以下代码:

from itertools import chain

lst = ['a', 1, 2, 3, 'b', 4, 5, 'c', 6]

def nestedForLoops():
    it = iter(lst)
    for item0 in it:
        if isinstance(item0, str):
            print(item0)
        else:
            # this shouldn't happen because of
            # 1. lst[0] is a str, and
            # 2. line A
            print(f"this shouldn't happen: {item0=}")
            pass
        for item1 in it:
            if not isinstance(item1, int):
                break
            print(f'\t{item1}')
        else: # no-break
            # reached end of iterator
            return
        # reached a str
        assert isinstance(item1, str)
        it = chain(item1, it) # line A

nestedForLoops()

我期待它打印

a
    1
    2
    3
b
    4
    5
c
    6

但它打印了

a
    1
    2
    3
this shouldn't happen: item0=4
this shouldn't happen: item0=5
c
this shouldn't happen: item0=6

我使用 while 循环而不是 for 循环编写了我认为等效的代码:

from itertools import chain

lst = ['a', 1, 2, 3, 'b', 4, 5, 'c', 6]

def nestedWhileLoops():
    it = iter(lst)
    while True:
        try:
            item0 = next(it)
        except StopIteration:
            break
        if isinstance(item0, str):
            print(item0)
        else:
            # this shouldn't happen because of
            # 1. lst[0] is a str, and
            # 2. line B
            print(f"this shouldn't happen: {item0=}")
            pass
        while True:
            try:
                item1 = next(it)
            except StopIteration:
                # reached end of iterator
                return
            if not isinstance(item1, int):
                break
            print(f'\t{item1}')
        # reached a str
        assert isinstance(item1, str)
        it = chain(item1, it) # line B

nestedWhileLoops()

它确实打印出我所期望的,即

a
    1
    2
    3
b
    4
    5
c
    6

那么为什么nestedForLoops 的行为与nestedWhileLoops 不同?

【问题讨论】:

标签: python nested iterator generator nested-loops


【解决方案1】:

一旦进入 for 循环,它正在使用的当前迭代器在到达该范围之外之前不能被重新分配。

这里:

for item0 in it:

您开始迭代 it 并在此 for 循环的范围内继续,直到函数结束。

当您在其范围内重新分配 it 时:

it = chain(item1, it) # line A

它对您已经迭代的迭代器没有影响。

它“有点”适用于内部 for 循环的原因是因为您退出了每个字符串上的 for 循环的范围。

简而言之,您的 for 循环示例执行以下操作:

  1. 进入外层for循环,开始迭代原来的it
  2. 进入内层for循环,继续迭代原来的it
  3. 退出内部for循环范围并重新分配it
  4. 在outer scope中,继续迭代原来的it
  5. 重新进入内部for循环并开始迭代新分配的it
  6. 重复 3 到 6

    做这样的事情可能会让你更好地理解:

    it = [1, 2, 3, 4]
    for item in it:
        print(item)
        it = None
    # 1
    # 2
    # 3
    # 4
    

【讨论】:

    【解决方案2】:

    for 循环包括对iter 的隐式调用以获取给定的迭代器可迭代的.一旦你得到那个迭代器,你不能修改它。分配一个新的可迭代到名称it 不会影响外层循环,因为外层循环只在循环时查看绑定到it 的值开始.

    通过 while 循环,您可以显式调用 next(it),这意味着您有机会在两次调用之间更改与名称 it 关联的值。 for 循环对next 的隐式调用不使用名称it 作为其参数;循环有它的自己的,对原始迭代器的私有引用,您不能修改该迭代器。 (chain(item, it) 创建一个新的迭代器,而不是修改当前引用的it。)

    【讨论】:

      猜你喜欢
      • 1970-01-01
      • 1970-01-01
      • 2020-11-19
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 2014-01-11
      • 1970-01-01
      • 2021-12-03
      相关资源
      最近更新 更多