编辑:
请参阅此问题底部的我的完整答案。
tl; dr回答:Python具有静态嵌套的作用域。静态
Aspect可以与隐式变量声明进行交互,从而产生非显而易见的结果。
(由于该语言通常具有动态特性,所以这尤其令人惊讶)。
我以为我对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()
,作用域不是全局的。编辑5:作为@user3022222 pointed out below,我在原始帖子中弄了这个例子。我相信这会失败,因为只有函数(其他代码块(如此类定义)无法访问封闭范围内的变量)。对于非功能代码块,只能访问局部变量,全局变量和内置变量。 this question中提供了更详尽的解释
多一个:
范例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不同?
我完全迷住了。请整理一下。
谢谢!
附言偶然地,这并不是我所理解的问题,我已经在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的答案!
供参考,来自the python docs:
重要的是要意识到范围
由文字决定:全球
在一个函数中定义的功能范围
module是该模块的名称空间,否
从哪里或通过什么别名的问题
函数被调用。另一方面,
实际搜索姓名已完成
在运行时动态地-但是,
语言定义正在演变
朝向静态名称解析
“编译”时间,所以不要依赖
动态名称解析! (事实上,
局部变量已经确定
静态地。)
编辑4
真正的答案
这种看似混乱的行为是由Python的statically nested scopes as defined in PEP 227引起的。实际上,它与PEP 3104无关。
从PEP 227:
名称解析规则是典型的
用于静态范围的语言[...]
[except]没有声明变量。
如果发生名称绑定操作
函数中的任何位置,然后使用该名称
被视为功能的局部
所有参考文献均指当地
捆绑。如果之前有参考
名称是绑定的,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
时,它将动态向外搜索,在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(谢天谢地)拒绝了。
作为一个有趣的旁注,静态嵌套范围实现了Python编译器进行的Alex Martelli has called最重要的单个优化:函数的局部变量不保存在dict中,它们在紧密的值向量中,每个局部变量访问使用该向量中的索引,而不是名称查找。”
最佳答案
这是Python名称解析规则的产物:您只能访问全局和局部作用域,而不能访问它们之间的作用域,例如不在您的直接外部范围内。
编辑:上面的措辞很差,您确实可以访问在外部作用域中定义的变量,但是通过从非全局名称空间执行x = x
或mymethod = mymethod
,您实际上是在用您的变量掩盖外部变量重新定义本地。
在示例2中,您的直接外部作用域是全局作用域,因此MyClass
可以看到mymethod
,但是在示例4中,您的直接外部作用域是my_defining_func()
,所以它不能,因为mymethod
的外部定义已被其本地定义掩盖。
有关非本地名称解析的更多详细信息,请参见PEP 3104。
另请注意,由于上述原因,我无法让示例3在Python 2.6.5或3.1.2下运行:
>>> 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
但是以下方法会起作用:
>>> def myfunc():
... x = 3
... class MyClass(object):
... y = x
... return MyClass
...
>>> myfunc().y
3
关于python - 在函数内创建类并访问包含函数范围内定义的函数,我们在Stack Overflow上找到一个类似的问题:https://stackoverflow.com/questions/56105768/