在函数中创建类并访问在包含函数的作用域中定义的函数

编辑 :

请在这个问题的最后看到我的完整答案。

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 中,它们在一个紧密的值向量中,并且每个局部变量访问都使用向量中的索引,而不是名称查找。”

90369 次浏览

That's an artifact of Python's name resolution rules: you only have access to the global and the local scopes, but not to the scopes in-between, e.g. not to your immediate outer scope.

EDIT: The above was poorly worded, you do have access to the variables defined in outer scopes, but by doing x = x or mymethod = mymethod from a non-global namespace, you're actually masking the outer variable with the one you're defining locally.

In example 2, your immediate outer scope is the global scope, so MyClass can see mymethod, but in example 4 your immediate outer scope is my_defining_func(), so it can't, because the outer definition of mymethod is already masked by its local definition.

See PEP 3104 for more details about nonlocal name resolution.

Also note that, for the reasons explained above, I can't get example 3 to run under either Python 2.6.5 or 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

But the following would work:

>>> def myfunc():
...     x = 3
...     class MyClass(object):
...         y = x
...     return MyClass
...
>>> myfunc().y
3

This post is a few years old, but it is among the rare ones to discuss the important problem of scope and static binding in Python. However, there is an important misunderstanding of the author for example 3 that might confuse readers. (do not take as granted that the other ones are all correct, it is just that I only looked at the issues raised by example 3 in details). Let me clarify what happened.

In example 3

def myfunc():
x = 3
class MyClass(object):
x = x
return MyClass


>>> myfunc().x

must return an error, unlike what the author of the post said. I believe that he missed the error because in example 1 x was assigned to 3 in the global scope. Thus a wrong understanding of what happened.

The explanation is extensively described in this post How references to variables are resolved in Python