编辑 :
请在这个问题的最后看到我的完整答案。
Python 具有静态嵌套的作用域 方面可以与隐式变量声明交互,产生不明显的结果。
(这可能特别令人惊讶,因为这种语言通常是动态的)。
我认为我已经很好地掌握了 Python 的范围规则,但是这个问题彻底地阻碍了我,我的 google-fu 也让我失望了(并不是我感到惊讶——看问题的标题;)
我将从一些按预期工作的示例开始,但是可以跳到示例4中有趣的部分。
例子一。
>>> 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 在下面指出的,这个函数似乎不起作用。
例子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()
,这个作用域不是全局的。
编辑5: 作为 @ user3022222如下所示,我在最初的帖子中修改了这个例子。我认为这是失败的,因为只有函数(而不是其他代码块,像这个类定义)可以访问封闭作用域中的变量。对于非函数代码块,只能访问局部、全局和内置变量。更详细的解释可以在 < a href = “ https://stackoverflow./questions/20246523/how-reference-to-variable-are-Resol- in-python”> 这个问题中找到
还有一件事:
例子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有什么不同?
我完全糊涂了,请把我理清楚。 谢谢!
另外,这可能不仅仅是我理解上的问题,我已经在 Python2.5.2和 Python2.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>
因此,看起来局部变量是在函数创建时定义的(这是成功的) ,导致本地名称被“保留”,因此在调用函数时屏蔽了外部作用域名称。
有意思。
谢谢弗雷德里克的回答!
作为参考,来自 巨蟒博士:
重要的是要认识到,范围 在文本上确定: 全局 定义的函数的作用域 模块是该模块的命名空间,不是 物质来自何处或用何种别名 函数。另一方面, 实际的名字搜索已经完成 动态地,在运行时ー然而, 语言的定义正在演变 实现静态名称解析,在 “编译”时,所以不要依赖 动态名称解析! (事实上, 局部变量已经确定 静止不动。)
编辑4
这种看似令人困惑的行为是由 Python 的 静态嵌套作用域,如 PEP227中定义的引起的,实际上它与 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 将 被抚养长大。
让我们运行 Tim 示例的两个简单版本:
>>> i = 6
>>> def f(x):
... def g():
... print i
... # ...
... # later
... # ...
... i = x
... g()
...
>>> f(3)
3
当 g()
在其内部作用域中找不到 i
时,它会动态地向外搜索,在 f
的作用域中找到 i
,这个作用域已经通过 i = x
赋值绑定到 3
。
但是改变 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 (谢天谢地)拒绝了。
作为一个有趣的旁注,静态嵌套作用域使得 Alex Martelli 打电话来了成为“ Python 编译器所做的最重要的一个优化: 函数的局部变量不保存在 dict 中,它们在一个紧密的值向量中,并且每个局部变量访问都使用向量中的索引,而不是名称查找。”