编辑 : 请参阅此问题底部的我的完整答案。 tl; dr答 :Python具有静态嵌套的作用域。的 静态 方面可以与隐变量声明相互作用,产生非显而易见的结果。 (由于该语言通常具有动态特性,所以这尤其令人惊讶)。
编辑 :
请参阅此问题底部的我的完整答案。
tl; dr答 :Python具有静态嵌套的作用域。的 静态 方面可以与隐变量声明相互作用,产生非显而易见的结果。
(由于该语言通常具有动态特性,所以这尤其令人惊讶)。
我以为我对Python的作用域规则掌握得很好,但是这个问题使我彻底陷入困境,而我的google-fu让我失败了(这并不令我感到惊讶-请看问题标题;)
我将从一些可以按预期工作的示例开始,但是请多跳到示例4。
范例1。
>>> x = 3 >>> class MyClass(object): ... x = x ... >>> MyClass.x 3
很简单:在类定义期间,我们能够访问外部(在本例中为全局)范围内定义的变量。
示例2
>>> def mymethod(self): ... return self.x ... >>> x = 3 >>> class MyClass(object): ... x = x ... mymethod = mymethod ... >>> MyClass().mymethod() 3
再次(暂时忽略 为什么 要这样做),这里没有什么意外的:我们可以访问外部作用域中的函数。
注意 :正如Frédéric在下面指出的那样,此功能似乎无效。请参阅示例5(及以后)。
范例3。
>>> def myfunc(): ... x = 3 ... class MyClass(object): ... x = x ... return MyClass ... >>> myfunc().x Traceback (most recent call last): File "<stdin>", line 1, in <module> File "<stdin>", line 3, in myfunc File "<stdin>", line 4, in MyClass NameError: name 'x' is not defined
基本上与示例1相同:我们正在从类定义中访问外部范围,这一次是由于范围不是全局的myfunc()。
myfunc()
编辑5: 正如下面的@ user3022222指出的那样,我在原始帖子中弄虚了这个示例。我相信这会失败,因为只有函数(其他代码块(如此类定义)无法访问封闭范围内的变量)。对于非功能代码块,只能访问局部变量,全局变量和内置变量。这个问题有更详尽的解释)
多一个:
范例4。
>>> def my_defining_func(): ... def mymethod(self): ... return self.y ... class MyClass(object): ... mymethod = mymethod ... y = 3 ... return MyClass ... >>> my_defining_func() Traceback (most recent call last): File "<stdin>", line 1, in <module> File "<stdin>", line 4, in my_defining_func File "<stdin>", line 5, in MyClass NameError: name 'mymethod' is not defined
嗯…不好意思
是什么使它与示例2不同?
我完全迷住了。请把我整理一下。谢谢!
PS偶然地认为这不仅仅是我的理解问题,我已经在Python 2.5.2和Python 2.6.2上进行了尝试。不幸的是,这些都是我目前可以使用的,但是它们都表现出相同的行为。
*根据 http://docs.python.org/tutorial/classes.html#python-scopes-and- namespaces进行 *编辑 :在执行期间的任何时候,至少有三个嵌套作用域可以直接访问其命名空间:
#4。似乎是第二个例子的反例。
编辑2
示例5
>>> def fun1(): ... x = 3 ... def fun2(): ... print x ... return fun2 ... >>> fun1()() 3
编辑3
正如@Frédéric指出的那样,对与外部作用域中相同名称的变量进行赋值似乎“屏蔽”了外部变量,从而阻止了赋值功能。
因此,示例4的此修改版本有效:
def my_defining_func(): def mymethod_outer(self): return self.y class MyClass(object): mymethod = mymethod_outer y = 3 return MyClass my_defining_func()
但是,这不是:
def my_defining_func(): def mymethod(self): return self.y class MyClass(object): mymethod_temp = mymethod mymethod = mymethod_temp y = 3 return MyClass my_defining_func()
我仍然不完全理解为什么会发生这种屏蔽:在分配发生时,名称绑定是否应该发生?
此示例至少提供了一些提示(以及更有用的错误消息):
>>> def my_defining_func(): ... x = 3 ... def my_inner_func(): ... x = x ... return x ... return my_inner_func ... >>> my_defining_func()() Traceback (most recent call last): File "<stdin>", line 1, in <module> File "<stdin>", line 4, in my_inner_func UnboundLocalError: local variable 'x' referenced before assignment >>> my_defining_func() <function my_inner_func at 0xb755e6f4>
因此看来,局部变量是在函数创建时定义的(成功),导致局部名称被“保留”,从而在调用函数时掩盖了外部作用域的名称。
有趣。
感谢Frédéric的答案!
供参考,来自python docs:
重要的是要认识到范围是由文本确定的:在模块中定义的函数的全局范围是该模块的名称空间,无论从何处调用该函数别名。另一方面,实际的名称搜索是在运行时动态完成的- 但是,语言定义正在“编译”时向静态名称解析发展,所以不要依赖动态名称解析!(实际上,局部变量已经是静态确定的。)
编辑4
这种看似令人困惑的行为是由PEP 227中定义的Python的静态嵌套范围引起的。实际上,它与PEP 3104没有关系。
从PEP 227:
名称解析规则通常用于静态范围的语言[…]未声明变量。如果名称绑定操作发生在函数中的任何位置,则该名称将被视为函数的本地名称,并且所有引用均引用本地绑定。如果在绑定名称之前发生引用,则会引发NameError。 […] 蒂姆·彼得斯(Tim Peters)的一个例子说明了在没有声明的情况下嵌套范围的潜在陷阱: i = 6 def f(x): def g(): print i # ... # skip to the next page # ... for i in x: # ah, i *is* local to f, so this is what g sees pass g() 对g()的调用将引用由for循环绑定到f()中的变量i。如果在执行循环之前调用了g(),则会引发NameError。
名称解析规则通常用于静态范围的语言[…]未声明变量。如果名称绑定操作发生在函数中的任何位置,则该名称将被视为函数的本地名称,并且所有引用均引用本地绑定。如果在绑定名称之前发生引用,则会引发NameError。
[…]
蒂姆·彼得斯(Tim Peters)的一个例子说明了在没有声明的情况下嵌套范围的潜在陷阱:
i = 6 def f(x): def g(): print i # ... # skip to the next page # ... for i in x: # ah, i *is* local to f, so this is what g sees pass g()
对g()的调用将引用由for循环绑定到f()中的变量i。如果在执行循环之前调用了g(),则会引发NameError。
让我们运行Tim示例的两个简单版本:
>>> i = 6 >>> def f(x): ... def g(): ... print i ... # ... ... # later ... # ... ... i = x ... g() ... >>> f(3) 3
当在其内部范围内g()找不到i时,它将动态向外搜索,找到iinf的范围,该范围已3通过i = x分配绑定。
g()
i
f
3
i = x
但是更改最后两个语句的顺序f会导致错误:
>>> i = 6 >>> def f(x): ... def g(): ... print i ... # ... ... # later ... # ... ... g() ... i = x # Note: I've swapped places ... >>> f(3) Traceback (most recent call last): File "<stdin>", line 1, in <module> File "<stdin>", line 7, in f File "<stdin>", line 3, in g NameError: free variable 'i' referenced before assignment in enclosing scope
记住PEP 227说过“名称解析规则是静态范围语言的典型代表”,让我们看一下(半)等效的C版本提供的内容:
// nested.c #include <stdio.h> int i = 6; void f(int x){ int i; // <--- implicit in the python code above void g(){ printf("%d\n",i); } g(); i = x; g(); } int main(void){ f(3); }
编译并运行:
$ gcc nested.c -o nested $ ./nested 134520820 3
因此,尽管C会很乐意使用一个未绑定的变量(使用之前存储在这里的任何东西:在这种情况下为134520820),但Python(谢天谢地)拒绝了。
作为一个有趣的旁注,静态嵌套范围实现了AlexMartelli所说的“Python编译器所做的最重要的单个优化:函数的局部变量不保存在dict中,它们存放在紧密的值向量中,并且每个本地变量访问使用该向量中的索引,而不是名称查找。”
这是Python名称解析规则的产物: 您只能访问全局和局部作用域,而不能访问它们之间的作用域,例如,不能访问您的直接外部作用域。
编辑: 上面的措辞很差,您 确实 可以访问在外部作用域中定义的变量,但是通过执行操作x = x或mymethod = mymethod从非全局名称空间访问,实际上是用您在本地定义的变量掩盖了外部变量。
x = x
mymethod = mymethod
在示例2中,您的直接外部作用域是全局作用域,因此MyClass可以看到mymethod,但是在示例4中,您的直接外部作用域是my_defining_func(),所以不能,因为的外部定义mymethod已经被其局部定义掩盖了。
MyClass
mymethod
my_defining_func()
有关非本地名称解析的更多详细信息,请参见PEP 3104。
另请注意,由于上述原因,我无法让示例3在Python 2.6.5或3.1.2下运行:
但是以下方法会起作用:
>>> def myfunc(): ... x = 3 ... class MyClass(object): ... y = x ... return MyClass ... >>> myfunc().y 3