【问题标题】:Nested for-loop: why is the inner loop only executed once?嵌套for循环:为什么内循环只执行一次?
【发布时间】:2017-09-20 05:16:06
【问题描述】:

我对在 python 中使用双 for 循环感到困惑,这是我的代码:

import numpy as np

range1 = np.linspace(1,6,10)
range2 = reversed(np.linspace(1,6,10))

for t1 in range1:
    print t1
    for t2 in range2:
        print t1,t2

输出是这样的:

1.0
1.0 6.0
1.0 5.44444444444
1.0 4.88888888889
1.0 4.33333333333
1.0 3.77777777778
1.0 3.22222222222
1.0 2.66666666667
1.0 2.11111111111
1.0 1.55555555556
1.0 1.0
1.55555555556
2.11111111111
2.66666666667
3.22222222222
3.77777777778
4.33333333333
4.88888888889
5.44444444444
6.0

它只对外循环的第一个值执行内循环,为什么会这样?如何让它遍历第一个和第二个变量的所有组合?

【问题讨论】:

    标签: python loops for-loop nested reverse


    【解决方案1】:

    reversed() 产生一个迭代器;一旦到达迭代器的末尾,就不能再使用它:

    >>> it = reversed([1, 2, 3])
    >>> list(it)
    [3, 2, 1]
    >>> list(it)
    []
    

    为嵌套循环创建一个新的迭代器:

    for t1 in range1:
        print t1
        for t2 in reversed(range1):
            print t1,t2
    

    reversed() documentation 链接到iterator glossary entry

    当没有更多数据可用时,会引发StopIteration 异常。 此时,迭代器对象已用尽,对其__next__() 方法的任何进一步调用只需再次引发StopIteration

    我的大胆强调。

    【讨论】:

    • 它并不总是生成器。如果类实现了__reversed__,它几乎可以返回任何东西。但是对于np.array,你是对的!
    • 嗯,在第一个内循环中使用 range1 会不会遇到同样的问题,而没有为外循环留下任何东西?
    • @MSeifert:__reversed__ documentation 要求返回一个迭代器。
    • @TimPietzcker:range() 是一个序列,而不是一个生成器,参见the documentationrange 类型代表一个不可变的数字序列
    • @Jérôme:完全正确。创建列表的完整副本具有reversed 避免的内存成本。当然,这可能是值得付出的代价。但是当你使用reversed()时,你每次都必须在循环中创建一个新的。
    【解决方案2】:

    在每个尊重 python 数据模型的实现中,reversed 的结果只能被耗尽一次(因为它应该返回一个在第一次遍历后被耗尽的迭代器)。在该迭代器用尽之后,它将不再有 yield 任何项目。但是你可以简单地使用切片来反转你的数组:

    range2 = np.linspace(1,6,10)[::-1]
    
    for t1 in range1:
        print t1
        for t2 in range2:
            print t1,t2
    

    numpy.arrays 的基本切片非常高效,甚至不需要复制原始文件。

    鉴于您使用arrays,您应该知道迭代它们是一个非常缓慢的操作,因为在迭代过程中每个值都需要拆箱。如果您确实需要遍历一维 arrays(提示:您通常不需要),您应该将它们转换为 lists:

    range1 = np.linspace(1,6,10).tolist()
    range2 = np.linspace(1,6,10)[::-1].tolist()
    

    因为与for-loop 中的(隐式)拆箱相比,tolist 在拆箱时效率更高。

    【讨论】:

      【解决方案3】:

      reversed 返回一个迭代器。一个迭代器只能使用一次。

      您在第一次迭代期间使用该迭代器。

      要多次使用迭代器,可以将其存储在list

      import numpy as np
      
      range1 = np.linspace(1,6,10)
      range2 = list(reversed(np.linspace(1,6,10)))  # store in list
      
      for t1 in range1:
          print t1
          for t2 in range2:
              print t1,t2
      

      迭代器的一个好处是永远不会计算整个列表。您可以在不创建整个 [0, 1, ..., 1000000] 列表的情况下迭代超过 100 万个元素,如果您的算法希望在满足条件时跳出循环,这会更好。

      在像 OP 这样的简单情况下,列表很短,并且您知道您将遍历所有元素,因此使用列表足够简单且高效。

      【讨论】:

        猜你喜欢
        • 1970-01-01
        • 2018-07-24
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        • 2022-06-15
        • 1970-01-01
        • 1970-01-01
        相关资源
        最近更新 更多