Python中的作用域';对于';循环

Python中的作用域';对于';循环,python,scope,Python,Scope,我不是问Python的范围规则;我大致了解Python中循环的作用域是如何工作的。我的问题是为什么设计决策是以这种方式做出的。例如(没有双关语): 上面将打印(9,2) 这让我觉得很奇怪:“foo”实际上只是控制循环,“bar”是在循环中定义的。我可以理解为什么“bar”必须在循环外部访问(否则,for循环的功能将非常有限)。我不明白的是,为什么在循环退出后控制变量必须保持在作用域内。根据我的经验,它只会使全局名称空间变得杂乱无章,并使跟踪其他语言的口译员可能捕获的错误变得更加困难。A因为这是在

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

上面将打印(9,2)

这让我觉得很奇怪:“foo”实际上只是控制循环,“bar”是在循环中定义的。我可以理解为什么“bar”必须在循环外部访问(否则,for循环的功能将非常有限)。我不明白的是,为什么在循环退出后控制变量必须保持在作用域内。根据我的经验,它只会使全局名称空间变得杂乱无章,并使跟踪其他语言的口译员可能捕获的错误变得更加困难。

A因为这是在使用
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。

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

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

更新

下面是关于这个话题的一个很好的讨论:

以前提出的关于循环的建议 循环的局部变量具有 偶然发现存在的问题 依赖于循环变量的代码 退出后保留其值 循环,看起来这是 被认为是可取的特征


简而言之,您可能会将其归咎于Python社区:Python的主要影响之一是,在荷兰开发的一种语言,用于向初学者教授编程概念。Python的创建者Guido van Rossum在20世纪80年代为ABC工作了几年。我对ABC几乎一无所知,但由于它是为初学者设计的,我想它的作用域一定是有限的,就像早期的基础知识一样。

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

在当前情况下:

# 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
变量不同。这不是最佳的或预期的行为。

如果循环中有一个break语句(并且希望稍后使用迭代值,可能是为了提取、索引某些内容或给出状态),它会为您节省一行代码和一个赋值,因此很方便。

这是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)

我很困惑-是什么阻止Python以函数作用域的方式对循环进行作用域划分?不是真的,只是语法没有变得疯狂。()“块是作为一个单元执行的一段Python程序文本。以下是块:模块、函数体和类定义…”@thebackhand,nothing。它只是被认为是不必要的。@thebackhand-在有块的语言中,对循环的作用域
是一个一般原则的自然延伸。在Python中,它必须是一种特殊情况,除非特殊情况有令人信服的好处,否则必须避免使用。如果您不希望
for
循环扰乱全局命名空间,请将其包装在函数中。闭包太多了!除非您在全局名称空间中运行循环(不常见),否则它会使本地名称空间混乱。如果不存在此名称空间,您以后将如何在循环中中断的点继续处理?在循环之前定义控制变量?@endolith是的。。。为什么不要求呢?人们只是喜欢他们习惯做的事。我想说,这类事情伤害了python程序员,他们已经习惯了这类事情,并且在切换到另一种语言时必须经历一个痛苦的过程。对于我们其他人来说,我想这是一条简洁的捷径。你可以在
else
子句中这样做,也就是说
else:print“我做了{0}次”。format(count)
-在局部作用域(Python中不存在)消失之前,第二个示例在Python 3中不起作用,对吗?第一个仍然有效吗?关于为什么从Python3中删除它的注释?对于计数,枚举(a,start=1)中的项:#默认索引来自零第一个示例,而不是一个好的用例,似乎更像是这个范围规则危险且不应该依赖的证据。如果
someiterator
为空怎么办?@Nas虽然在这种情况下可以使用
else
子句,但它通常不起作用,因为循环体可能会
过早中断。不回答问题。OP询问的是foo,而不是total(在他们的例子中是bar)。@JamesBradbury
total
foo
在OP的场景中仍然有循环本地绑定,逻辑是相同的。OP:“我能理解为什么需要在外部访问“bar”
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)