【问题标题】:Same variable name for different values in nested loops.嵌套循环中不同值的相同变量名。
【发布时间】:2012-11-06 16:38:08
【问题描述】:

这段代码是完全有效的 Python

x=[[1,2,3,4], [11,22,33,44]]
for e in x:
    for e in e:
        print e

谁能告诉我为什么,以及它是如何工作的?

我知道e 都在不同的范围内,但是为什么像这样一起使用它们不会导致错误?

【问题讨论】:

  • 为什么导致错误?
  • 它会导致什么错误,它应该是什么?
  • 哈!好问题。这是我在调试 Python 并注意到我做到了之前才意识到的事情......我担心我做了一些可怕的事情,但结果很好。仍然不太确定我对它“合法”的看法。

标签: python


【解决方案1】:

范围没有什么不同;在 Python 中,函数具有单个本地范围(就像在控制台中在全局级别输入的代码一样)。

代码正常的原因是您在将e 的外部值重新绑定到内部值之前完成了它;尝试查看打印的内容:

x=[[1,2,3,4], [11,22,33,44]]
for e in x:
    for e in e:
        print e
    print e

【讨论】:

    【解决方案2】:

    e 只是一个标签。外循环的每次迭代,e 都被分配了来自xnth 值,并且每次内循环迭代都被分配了来自x[n]mth 值。这是完全有效的 Python 代码,只是从样式的角度来看是不可取的,因为除了一个简单的示例之外,它很快就会混淆 e 在代码中的什么位置代表什么,因此很可能会导致错误。

    【讨论】:

      【解决方案3】:

      我想您可以将上述表达式中的内部循环大致翻译为:

      for e in x:
          ee = iter(e)
          try:
              e = next(ee)
              while True
                  print e
                  e = next(ee)
          except StopIteration
              pass
      

      注意这里的关键是在语句中:for e in ...... 通过iterator protocol 转换为迭代器。您实际迭代的对象 是与您最初提供的 e 不同的对象。由于它是一个单独的对象(与其名称分开存储在当前范围内以允许对其进行迭代),因此在当前范围内将新变量绑定到该名称没有问题——也许我应该说没有问题除了它使代码真的很难遵循。

      这实际上与您执行此操作没有问题的原因相同:

      A = [['foo']]  #Define A
      b = A[0]       #Take information from A and rebind it to something else
      c = A          #We can even take the entire reference and bind/alias it to a new name.
      A = 'bar'      #Re-assign A -- Python doesn't care that A already existed.
      

      这里还有一些需要考虑的事情:

      x = [1,2,3,4]
      for a in x:
          print a
          next(a)   #Raises an error because lists aren't iterators!
      

      现在很少使用,(但有时是必要的)成语:

      x = [1,2,3,4]
      y = iter(x)   #create an iterator from the list x
      for a in y:
          print a
          #This next line is OK.  
          #We also consume the next value in the loop since `iter(y)` returns `y`!
          #In fact, This is the easiest way to get a handle on the object you're
          #actually iterating over.
          next(y)   
      

      终于:

      x = [1,2,3,4]
      y = iter(x)   #create an iterator from the list x
      for a in y:
          print a
          #This effectively does nothing to your loop because you're rebinding
          #a local variable -- You're not actually changing the iterator you're
          #iterating over, just as `A = 'bar'` doesn't change the value of
          #the variable `c` in one of the previous examples.
          y = iter(range(10))
      

      【讨论】:

      • 我认为这个答案有点误导,因为它似乎暗示有多个范围。
      • @NullUserException -- 它如何暗示有不同的范围?如果您对具体令人困惑的内容有任何建议,我很乐意更新。我试图说明的一点是,在评估for 循环时,python 从最右边的对象构造了一个迭代器,并将其用于循环的其余部分,你做什么都没关系持有对用于构造迭代器的对象的引用的变量名。不幸的是,没有 clean 语义(我能想到的)可以更好地表达...
      • @NullUserException -- 在某些方面,您实际迭代的迭代器确实存在于不同的范围内,因为除非iter(obj) 在中返回obj,否则您无法处理它在这种情况下,你有一个句柄只是因为你有一个句柄 obj
      • 很好地说明了 for 循环的内部工作原理。脚注:为了进一步确认这一点,请尝试反汇编 for 循环;您将看到GET_ITER 操作码,它有效地调用堆栈顶部的iter
      • @senderle -- 我已经很久没有真正考虑过 for 循环是如何工作的了(也许一年前我真的尝试过用 __iter__... 开设课程)。意识到我现在对所有这些东西的工作原理比以前有了更好的了解,这很有趣。
      【解决方案4】:

      因为第二个 e 在第一个 e 评估为列表之后绑定。因此,所有其他迭代步骤都不是从变量中获取项目,而是从列表中获取项目。例如,在下一个代码中重新赋值为 e 对迭代没有影响:

      for e in x:
          for i in e:
              print i
              e = [8, 8, 8]
      

      【讨论】:

        【解决方案5】:

        坦率地说,我没有找到任何令人满意的当前答案。我认为根本的解释是 Python 将 for 循环作用域作为一种特殊情况处理。

        看起来 Python 认识到 for 循环变量是特殊的,并且在 for 循环结束时,根据需要将变量的当前值复制到封闭范围。

        这解释了为什么可以编写以下代码:

        for i in range(3):
            i = 5
            print("hello")
        

        它会执行 3 次。 for 循环变量 i 是 specialdistinct 与分配值 5 的变量 i。

        Python 可能将其视为:

        for i_12349678 in range(3):
            i = 5
            print("hello")
        

        对于使用相同变量 i 的嵌套 for 循环:

        for i in range(3):
            for i in range(4):
                print("i = %s" % i)
        

        Python 可能认为它更像:

        for i_18987982221 in range(3):
            for i_9870272721 in range(4):
                print("i = %s" % i_9870272721)
        

        更多信息: http://mail.python.org/pipermail/python-ideas/2008-October/002109.html

        【讨论】:

          【解决方案6】:

          'e' 在这种情况下是在相同的范围内。如果你做类似...

          for i in range(MAX):
              if(some_condition == True):
                  for i in range(5):
                      #do stuff
          

          如果代码遍历到内部 for 循环,它会将“外部 i”递增 5 次,导致您跳过这些运行。

          使用您发布的代码,它不会产生语法错误,而且从逻辑角度来看,它恰好可以解决,但可能存在其他示例,您会得到错误的结果。

          【讨论】:

            猜你喜欢
            • 1970-01-01
            • 1970-01-01
            • 1970-01-01
            • 1970-01-01
            • 1970-01-01
            • 1970-01-01
            • 1970-01-01
            • 1970-01-01
            • 1970-01-01
            相关资源
            最近更新 更多