【问题标题】:Scoping in Python 'for' loopsPython 'for' 循环中的作用域
【发布时间】:2011-04-06 10:06:24
【问题描述】:

我不是在问 Python 的作用域规则;我一般了解作用域在 Python for 循环中的工作原理。我的问题是为什么以这种方式做出设计决策。例如(没有双关语):

for foo in xrange(10):
    bar = 2
print(foo, bar)

上面将打印 (9,2)。

这让我觉得很奇怪:“foo”实际上只是控制循环,而“bar”是在循环内部定义的。我可以理解为什么可能有必要在循环外部访问“bar”(否则,for 循环的功能将非常有限)。我不明白为什么循环退出后控制变量必须保持在范围内。根据我的经验,它只会使全局命名空间变得混乱,并且更难追踪可能被其他语言的解释器捕获的错误。

【问题讨论】:

  • 如果您不希望for 循环弄乱您的全局命名空间,请将其包装在一个函数中。大量关闭!
  • 除非您在全局命名空间中运行循环(不常见),否则它会使 local 命名空间变得混乱。
  • 如果这不存在,你将如何在你离开循环后继续处理?只定义控制变量之前循环?
  • @endolith 是的...为什么不需要呢?
  • 好吧,人们只会更喜欢他们习惯做的事情。我想说这种事情伤害了习惯这种事情并且在切换到另一种语言时必须经历一个痛苦的过程的 python 编码器。对于我们其他人来说,我想这是一个简洁的小捷径。

标签: python scope


【解决方案1】:

最可能的答案是它只是保持语法简单,并没有成为采用的绊脚石,并且许多人很高兴在循环构造中分配名称时不必消除名称所属范围的歧义.变量不在范围内声明,它由赋值语句的位置暗示。 global 关键字就是因为这个原因而存在(表示分配是在全局范围内完成的)。

更新

这里有一个很好的讨论主题:http://mail.python.org/pipermail/python-ideas/2008-October/002109.html

以前提出的制作for循环的建议 循环局部变量有 偶然发现存在的问题 依赖循环变量的代码 退出后保持其值 循环,似乎这是 被视为理想的功能。

简而言之,您可以将其归咎于 Python 社区:P

【讨论】:

  • 如果归纳变量的范围被限制在循环体中,语法会变得更复杂吗?这种变化将仅限于 Python 中的语义分析,而不是其语法。
  • 循环不是 Python 中的块。这种行为改变需要从根本上改变语法或提供特殊情况。归纳变量的整个概念也没有在当前语法中表达出来。语法提供了解释器如何解释的合同。我的观点是,我无法预见如何在不使语法更复杂的情况下改变这种行为。由于设计决策的副作用已成为一项功能,因此这一切都没有实际意义。
  • 这篇帖子mail.python.org/pipermail/python-dev/2005-September/056677.html 提供了有关布朗先生所暗示的速度和复杂性的更多细节。
  • 这对我的概念也有很大帮助:eli.thegreenplace.net/2015/…
【解决方案2】:

Python 没有块,其他一些语言(例如 C/C++ 或 Java)也是如此。因此,Python 中的作用域单元是一个函数。

【讨论】:

  • 我很困惑 - 是什么阻止 Python 以与函数作用域相同的方式为循环定义作用域?
  • 不是真的,只是语法不会变得疯狂。 (docs.python.org/reference/…) "块是作为一个单元执行的一段 Python 程序文本。以下是块:模块、函数体和类定义..."
  • @thebackhand,什么都没有。它只是被认为是不必要的。
  • @thebackhand - 在带有块的语言中,范围 for 循环是一般原则的自然扩展。在 Python 中,它必须是一个特例,除非它们有令人信服的好处,否则应避免特例。
【解决方案3】:

really useful case 是在使用 enumerate 时,您希望最后的总数:

for count, x in enumerate(someiterator, start=1):
    dosomething(count, x)
print "I did something {0} times".format(count)

这有必要吗?没有。但是,确实很方便。

需要注意的另一件事:在 Python 2 中,列表推导中的变量也会泄露:

>>> [x**2 for x in range(10)]
[0, 1, 4, 9, 16, 25, 36, 49, 64, 81]
>>> x
9

但是,这不适用于 Python 3。

【讨论】:

  • 你可以在else 子句中做到这一点,即。 else: print "I did something {0} times".format(count) - 在本地范围(在 Python 中不存在)消失之前
  • 只有第二个示例在 Python 3 中不起作用,对吧?第一个还在吗?为什么要从 Python 3 中删除它?
  • for count, item in enumerate(a, start=1): # 默认索引从零开始
  • 第一个例子不是一个好的用例,更像是这个范围规则是危险的并且不应该依赖的证据。如果someiterator 为空怎么办?
  • @Nas 虽然在这种情况下可以使用else 子句,但它通常不起作用,因为循环体可能会过早地break
【解决方案4】:

对 Python 的主要影响之一是 ABC,这是一种在荷兰开发的用于向初学者教授编程概念的语言。 Python 的创建者 Guido van Rossum 在 1980 年代在 ABC 工作了几年。我对 ABC 几乎一无所知,但由于它是为初学者设计的,我想它的作用域一定是有限的,就像早期的 BASIC 一样。

【讨论】:

    【解决方案5】:

    如果你在循环中有一个 break 语句(并且想稍后使用迭代值,也许是为了恢复、索引某些东西或给出状态),它会为你节省一行代码和一个赋值,所以有一个方便。

    【讨论】:

      【解决方案6】:

      这是 Python 中的一种设计选择,它通常使某些任务比其他具有典型块作用域行为的语言更容易。

      但通常你仍然会错过典型的块作用域,因为,比如说,你可能有应该尽快释放的大型临时数组。它可以通过临时函数/类技巧来完成,但仍然可以通过直接操作解释器状态来实现更简洁的解决方案。

      from scoping import scoping
      a = 2 
      
      with scoping():
          assert(2 == a)
          a = 3
          b = 4
          scoping.keep('b')
          assert(3 == a) 
      
      assert(2 == a) 
      assert(4 == b)
      

      https://github.com/l74d/scoping

      【讨论】:

        【解决方案7】:

        对于初学者来说,如果变量是循环的局部变量,那么这些循环对于大多数实际编程来说都是无用的。

        目前情况:

        # Sum the values 0..9
        total = 0
        for foo in xrange(10):
            total = total + foo
        print total
        

        产生45。现在,考虑一下在 Python 中赋值是如何工作的。如果循环变量是严格本地的:

        # Sum the values 0..9?
        total = 0
        for foo in xrange(10):
            # Create a new integer object with value "total + foo" and bind it to a new
            # loop-local variable named "total".
            total = total + foo
        print total
        

        产生0,因为赋值后循环内的total与循环外的total不同。这不是最佳或预期的行为。

        【讨论】:

        • 不回答问题。 OP询问的是foo,而不是total(在他们的例子中是bar)。
        • @JamesBradbury totalfoo 在 OP 的场景中仍然具有循环本地绑定,并且逻辑是相同的。
        • OP:“我可以理解为什么'bar'可能需要在循环外访问(否则,for循环的功能将非常有限)。我不明白的是为什么控制变量在循环退出后保持在范围内是必要的。” (强调我的)
        • @JamesBradbury 你可能是对的,但我三年前回答过这个问题,现在可能不值得争论。
        猜你喜欢
        • 2015-02-06
        • 2018-07-24
        • 2023-03-26
        • 2015-12-13
        • 1970-01-01
        • 2020-10-18
        • 1970-01-01
        • 2018-09-09
        相关资源
        最近更新 更多