在函数内创build一个类并访问在包含函数的作用域中定义的函数

编辑

在这个问题的底部看到我的完整答案。

tl; dr答案 :Python有静态嵌套的作用域。 静态方面可以与隐式variables声明交互,产生不明显的结果。

(由于语言的dynamic性,这可能会特别令人惊讶)。

我认为我对Python的范围规则有一个很好的处理,但是这个问题让我彻底陷入困境,而且我的google-fu已经失败了(不是我很惊讶 – 看问题的标题;)

我将从几个按预期工作的例子开始,但可以跳到例4的多汁部分。

例1

>>> x = 3 >>> class MyClass(object): ... x = x ... >>> MyClass.x 3 

直截了当地说:在类定义期间,我们可以访问在外层(在这种情况下为全局)范围内定义的variables。

例2。

 >>> def mymethod(self): ... return self.x ... >>> x = 3 >>> class MyClass(object): ... x = x ... mymethod = mymethod ... >>> MyClass().mymethod() 3 

再次(忽略为什么可能要这样做),这里没有什么意外的:我们可以访问外部范围中的函数。

注意 :正如Frédéric在下面指出的那样,这个function似乎不起作用。 请参阅示例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指出的 ,我在我原来的post中拙劣了这个例子。 我相信这会失败,因为只有函数(而不是其他代码块,如此类定义)才能访问封闭范围中的variables。 对于非function代码块,只能访问本地,全局和内置variables。 在这个问题上有更详尽的解释

多一个:

例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。 不幸的是,这些都是我目前能够访问的,但是它们都performance出相同的行为。

编辑根据http://docs.python.org/tutorial/classes.html#python-scopes-and-namespaces :在执行过程中的任何时候,至less有三个嵌套的作用域是可以直接访问的:

  • 最先search的最里面的范围包含本地名称
  • 从最近的封闭范围开始search的任何封闭函数的范围包含非本地名称,也包含非全局名称
  • 倒数第二个作用域包含当前模块的全局名称
  • 最后一个范围(最后search)是包含内置名称的名称空间

#4。 似乎是第二个这样的反例。

编辑2

例5

 >>> def fun1(): ... x = 3 ... def fun2(): ... print x ... return fun2 ... >>> fun1()() 3 

编辑3

正如@Frédéric所指出的那样,赋值给外部variables名的variables似乎“掩盖”了外部variables,从而阻止赋值工作。

所以例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() 

我仍然不完全明白为什么会发生这种掩蔽:在赋值发生时不应该发生名称绑定?

这个例子至less提供了一些提示(和一个更有用的错误信息):

 >>> 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> 

所以看起来局部variables是在函数创build(成功)时定义的,导致本地名称被“保留”,因此在调用该函数时掩盖了外部作用域名称。

有趣。

感谢Frédéric的回答(s)!

作为参考,从python文档 :

意识到范围是由文本决定的:在模块中定义的函数的全局范围是该模块的名称空间,而不pipe从哪里或通过别名调用函数。 另一方面,名字的实际search是在运行时dynamic完成的 – 然而,语言定义正在向“编译”时间的静态名称parsing发展,所以不要依赖dynamic名称parsing! (事实上​​,局部variables已经静态确定了。)

编辑4

真正的答案

这个表面上令人困惑的行为是由PEP 227中定义的Python 静态嵌套作用域引起的。 它实际上与PEP 3104无关。

从PEP 227:

名称parsing规则是静态范围语言的典型[…] [除了]variables没有声明。 如果名称绑定操作发生在函数中的任何位置,则该名称将被视为该函数的本地,并且所有引用都引用本地绑定。 如果在绑定名称之前发生引用,则会引发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()中绑定的variables。 如果在执行循环之前调用g(),则会引发NameError。

让我们运行两个比较简单的Tim例子:

 >>> i = 6 >>> def f(x): ... def g(): ... print i ... # ... ... # later ... # ... ... i = x ... g() ... >>> f(3) 3 

g()在它的内部范围中没有findi时,它dynamic地向外search,通过i = x赋值find在f范围内的i ,它已经被绑定到了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表示“名称parsing规则对于静态范围的语言是典型的”,可​​以看看(半)等价的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会高兴地使用一个未绑定的variables(使用前面存储的任何东西:134520820,在这种情况下),Python(谢天谢地)拒绝。

作为一个有趣的方面,静态嵌套的作用域使得Alex Martelli所说的 “Python编译器所做的最重要的优化:函数的局部variables不被保存在一个字典中,它们处于一个紧密的值向量中,每个局部variables访问使用该向量中的索引,而不是名称查找。

这是Python的名称parsing规则的一个人为因素: 您只能访问全局和本地范围,而不能访问范围之间的范围,例如不能访问您的直接外部范围。

编辑:上面是措辞不佳,你有权访问外部作用域中定义的variables,但通过从非全局命名空间x = xmymethod = mymethod ,你实际上掩盖外部variables,重新定义本地。

在示例2中,您的直接外部作用域是全局作用域,所以MyClass可以看到mymethod ,但在示例4中,您的直接外部作用域是my_defining_func() ,所以它不能,因为mymethod的外部定义已经被本地定义。

有关非本地名称parsing的更多详细信息,请参阅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中讨论范围和静态绑定的重要问题还是很less见的。 然而,对于作者的一个重要的误解,例如,可能会使读者感到困惑。 (不要以为其他的都是正确的,只是我只看了例三提出的问题)。 让我澄清发生了什么事。

在例3中

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

必须返回一个错误,不同于该post的作者所说的。 我相信他错过了这个错误,因为在全球范围内,例子1被分配到了3 。 因此,对发生了什么错误的理解。

在这篇文章中对这个解释进行了广泛的描述如何在Python中parsingvariables的引用