Creating a class within a function and access a function defined in the containing function's scope
Edit:
See my full answer at the bottom of this question.
tl;dr answer: Python has statically nested scopes. The static
aspect can interact with the implicit variable declarations, yielding non-obvious results.(This can be especially surprising because of the language's generally dynamic nature).
我认为我对python的范围规则有很好的理解,但这个问题让我彻底陷入困境,我的google fu也让我失望了(我并不感到惊讶-看看问题标题;)
我将从一些如预期一样有效的例子开始,但对于多汁的部分,请随意跳到示例4。
例1。
1 2 3 4 5 6 | >>> x = 3 >>> class MyClass(object): ... x = x ... >>> MyClass.x 3 |
足够简单:在类定义期间,我们能够访问外部(在本例中是全局)范围中定义的变量。
例2。
1 2 3 4 5 6 7 8 9 10 | >>> def mymethod(self): ... return self.x ... >>> x = 3 >>> class MyClass(object): ... x = x ... mymethod = mymethod ... >>> MyClass().mymethod() 3 |
再一次(暂时忽略了为什么要这样做),这里没有什么意外的:我们可以访问外部作用域中的函数。
注:正如fr_d_ric在下面指出的,这个函数似乎不起作用。请参阅示例5(及更高版本)。
例3。
1 2 3 4 5 6 7 8 9 10 11 12 | >>> 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相同:我们从类定义内访问外部作用域,只是这次该作用域不是全局的,多亏了
编辑5:正如@user3022222下面指出的那样,我在最初的文章中把这个例子弄错了。我认为这是失败的,因为只有函数(而不是其他代码块,如类定义)才能访问封闭范围中的变量。对于非函数代码块,只能访问局部、全局和内置变量。在这个问题上有更详尽的解释。
再一个:
例4。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 | >>> 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和python2.6.2上尝试过这一方法,但这并不是我理解中的问题。不幸的是,这些都是我目前能接触到的,但它们都表现出相同的行为。
编辑根据http://docs.python.org/tutorial/classes.html python作用域和名称空间:在执行期间的任何时候,至少有三个嵌套作用域的名称空间可以直接访问:
- 最里面的范围,即首先搜索,包含本地姓名
- 任何封闭的范围搜索的函数从最近的围墙开始范围,包含非本地,但也包含非全局名称
- next to last作用域包含当前模块的全局名称
- 最外面的作用域(最后搜索)命名空间是否包含内置的姓名
4。似乎是第二个例子的反例。
编辑2
例5。
1 2 3 4 5 6 7 8 | >>> def fun1(): ... x = 3 ... def fun2(): ... print x ... return fun2 ... >>> fun1()() 3 |
编辑3
正如@fr_d_ric指出的那样,对外部作用域中同名变量的赋值似乎"屏蔽"了外部变量,从而阻止了赋值的工作。
因此,示例4的这个修改版本可以工作:
1 2 3 4 5 6 7 8 9 | def my_defining_func(): def mymethod_outer(self): return self.y class MyClass(object): mymethod = mymethod_outer y = 3 return MyClass my_defining_func() |
但这并不是:
1 2 3 4 5 6 7 8 9 10 | 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() |
我仍然不完全理解为什么会出现这种屏蔽:当分配发生时,名称绑定不应该发生吗?
此示例至少提供了一些提示(以及更有用的错误消息):
1 2 3 4 5 6 7 8 9 10 11 12 13 14 | >>> 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> |
因此,似乎局部变量是在函数创建时定义的(成功),从而导致局部名称被"保留",从而在调用函数时屏蔽外部作用域名称。
有趣。
感谢您的回答!
供参考,来自python文档:
It is important to realize that scopes
are determined textually: the global
scope of a function defined in a
module is that module’s namespace, no
matter from where or by what alias the
function is called. On the other hand,
the actual search for names is done
dynamically, at run time — however,
the language definition is evolving
towards static name resolution, at
"compile" time, so don’t rely on
dynamic name resolution! (In fact,
local variables are already determined
statically.)
编辑4
真正的答案这种看似令人困惑的行为是由pep 227中定义的python静态嵌套作用域引起的。它实际上与PEP 3104无关。
来自PEP 227:
The name resolution rules are typical
for statically scoped languages [...]
[except] variables are not declared.
If a name binding operation occurs
anywhere in a function, then that name
is treated as local to the function
and all references refer to the local
binding. If a reference occurs before
the name is bound, a NameError is
raised.[...]
An example from Tim Peters demonstrates the potential pitfalls of
nested scopes in the absence of declarations:
1
2
3
4
5
6
7
8
9
10 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()The call to g() will refer to the variable i bound in f() by the for
loop. If g() is called before the loop is executed, a NameError will
be raised.
让我们运行两个更简单版本的Tim示例:
1 2 3 4 5 6 7 8 9 10 11 12 | >>> i = 6 >>> def f(x): ... def g(): ... print i ... # ... ... # later ... # ... ... i = x ... g() ... >>> f(3) 3 |
当
但是更改顺序
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 | >>> 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版本提供的:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 | // nested.c #include <stdio.h> int i = 6; void f(int x){ int i; // <--- implicit in the python code above void g(){ printf("%d ",i); } g(); i = x; g(); } int main(void){ f(3); } |
编译并运行:
1 2 3 4 | $ gcc nested.c -o nested $ ./nested 134520820 3 |
所以,虽然C很乐意使用未绑定的变量(使用之前存储在这里的任何变量:134520820,在本例中),但python(谢天谢地)拒绝了。
有趣的一点是,静态嵌套的作用域实现了Alex Martelli所说的"Python编译器所做的最重要的优化:函数的局部变量不保存在dict中,它们位于值的紧向量中,并且每个局部变量访问都使用该向量中的索引,而不是名称查找。"
这是python名称解析规则的产物:您只能访问全局和本地作用域,但不能访问中间的作用域,例如,不能访问直接外部作用域。
编辑:上面的措辞不好,您确实可以访问在外部作用域中定义的变量,但是通过从非全局命名空间执行
在示例2中,您的直接外部作用域是全局作用域,所以
有关非本地名称解析的更多详细信息,请参阅PEP 3104。
还要注意,由于上面解释的原因,我不能让示例3在python 2.6.5或3.1.2下运行:
1 2 3 4 5 6 7 8 9 10 11 12 | >>> 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 2 3 4 5 6 7 8 | >>> def myfunc(): ... x = 3 ... class MyClass(object): ... y = x ... return MyClass ... >>> myfunc().y 3 |
这篇文章已有几年的历史,但很少讨论Python中的范围和静态绑定的重要问题。然而,作者有一个重要的误解,例如3,可能会混淆读者。(不要想当然地认为其他的都是正确的,我只是详细地看了示例3提出的问题)。让我澄清一下发生了什么。
例3
1 2 3 4 5 6 7 | def myfunc(): x = 3 class MyClass(object): x = x return MyClass >>> myfunc().x |
必须返回一个错误,不像文章作者所说的那样。我认为他遗漏了这个错误,因为在示例1中,
这篇文章对这一解释作了广泛的描述。如何在python中解析对变量的引用