Python 在函数内创建类并访问在包含函数的作用域中定义的函数
声明:本页面是StackOverFlow热门问题的中英对照翻译,遵循CC BY-SA 4.0协议,如果您需要使用它,必须同样遵循CC BY-SA许可,注明原文地址和作者信息,同时你必须将它归于原作者(不是我):StackOverFlow
原文地址: http://stackoverflow.com/questions/4296677/
Warning: these are provided under cc-by-sa 4.0 license. You are free to use/share it, But you must attribute it to the original authors (not me):
StackOverFlow
Creating a class within a function and access a function defined in the containing function's scope
提问by Gabriel Grant
Edit:
See my full answer at the bottom of this question.
tl;dr answer: Python has statically nested scopes. The staticaspect can interact with the implicit variable declarations, yielding non-obvious results.
(This can be especially surprising because of the language's generally dynamic nature).
编辑:
在这个问题的底部查看我的完整答案。
tl;dr 回答:Python 具有静态嵌套的作用域。的静态方面可以与隐变量声明相互作用,产生非显而易见的结果。
(这可能特别令人惊讶,因为该语言通常具有动态特性)。
I thought I had a pretty good handle on Python's scoping rules, but this problem has me thoroughly stymied, and my google-fu has failed me (not that I'm surprised - look at the question title ;)
我以为我对 Python 的范围规则有很好的掌握,但是这个问题让我彻底陷入困境,而我的 google-fu 也让我失望了(不是让我感到惊讶 - 看看问题标题;)
I'm going to start with a few examples that work as expected, but feel free to skip to example 4 for the juicy part.
我将从几个按预期工作的示例开始,但请随意跳到示例 4 以了解多汁的部分。
Example 1.
示例 1。
>>> x = 3
>>> class MyClass(object):
... x = x
...
>>> MyClass.x
3
Straightforward enough: during class definition we're able to access the variables defined in the outer (in this case global) scope.
非常简单:在类定义期间,我们能够访问在外部(在这种情况下为全局)范围中定义的变量。
Example 2.
示例 2。
>>> def mymethod(self):
... return self.x
...
>>> x = 3
>>> class MyClass(object):
... x = x
... mymethod = mymethod
...
>>> MyClass().mymethod()
3
Again (ignoring for the moment whyone might want to do this), there's nothing unexpected here: we can access functions in the outer scope.
同样(暂时忽略为什么要这样做),这里没有什么意外:我们可以访问外部作用域中的函数。
Note: as Frédéric pointed out below, this function doesn't seem to work. See Example 5 (and beyond) instead.
注意:正如 Frédéric 在下面指出的,这个功能似乎不起作用。请参见示例 5(及其他)。
Example 3.
例 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
This is essentially the same as example 1: we're accessing the outer scope from within the class definition, just this time that scope isn't global, thanks to myfunc().
这与示例 1 基本相同:我们从类定义中访问外部作用域,只是这次该作用域不是全局的,感谢myfunc().
Edit 5:As @user3022222 pointed out below, I botched this example in my original posting. I believe this fails because only functions (not other code blocks, like this class definition) can access variables in the enclosing scope. For non-function code blocks, only local, global and built-in variables are accessible. A more thorough explanation is available in this question
编辑 5:正如@user3022222 在下面指出的那样,我在原来的帖子中把这个例子搞砸了。我相信这会失败,因为只有函数(而不是其他代码块,如此类定义)可以访问封闭范围内的变量。对于非功能代码块,只能访问局部、全局和内置变量。在这个问题中有一个更彻底的解释
One more:
多一个:
Example 4.
例 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
Um...excuse me?
嗯……对不起?
What makes this any different from example 2?
这与示例 2 有何不同?
I'm completely befuddled. Please sort me out. Thanks!
我完全糊涂了。请帮我整理一下。谢谢!
P.S. on the off-chance that this isn't just a problem with my understanding, I've tried this on Python 2.5.2 and Python 2.6.2. Unfortunately those are all I have access to at the moment, but they both exhibit the same behaviour.
PS,这不仅仅是我理解的问题,我已经在 Python 2.5.2 和 Python 2.6.2 上尝试过。不幸的是,我目前只能访问这些,但它们都表现出相同的行为。
EditAccording to http://docs.python.org/tutorial/classes.html#python-scopes-and-namespaces: at any time during execution, there are at least three nested scopes whose namespaces are directly accessible:
编辑根据http://docs.python.org/tutorial/classes.html#python-scopes-and-namespaces:在执行过程中的任何时候,至少有三个嵌套的作用域,其命名空间是可直接访问的:
- the innermost scope, which is searched first, contains the local names
- the scopes of any enclosing functions, which are searched starting with the nearest enclosing scope, contains non-local, but also non-global names
- the next-to-last scope contains the current module's global names
- the outermost scope (searched last) is the namespace containing built-in names
- 首先搜索的最里面的范围包含本地名称
- 从最近的封闭范围开始搜索的任何封闭函数的范围包含非本地名称,但也包含非全局名称
- 倒数第二个范围包含当前模块的全局名称
- 最外面的范围(最后搜索)是包含内置名称的命名空间
#4. seems to be a counter-example to the second of these.
#4. 似乎是其中第二个的反例。
Edit 2
编辑 2
Example 5.
例 5。
>>> def fun1():
... x = 3
... def fun2():
... print x
... return fun2
...
>>> fun1()()
3
Edit 3
编辑 3
As @Frédéric pointed out the assignment of to a variable of the same name as it has in the outer scope seems to "mask" the outer variable, preventing the assignment from functioning.
正如@Frédéric 指出的那样,分配给与外部作用域中同名的变量似乎“掩盖”了外部变量,从而阻止了赋值的运行。
So this modified version of Example 4 works:
因此,示例 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()
However this doesn't:
然而,这不会:
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()
I still don't fully understand why this masking occurs: shouldn't the name binding occur when the assignment happens?
我仍然不完全理解为什么会发生这种掩码:分配发生时不应该发生名称绑定吗?
This example at least provides some hint (and a more useful error message):
这个例子至少提供了一些提示(以及更有用的错误信息):
>>> 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>
So it appears that the local variable is defined at function creation (which succeeds), resulting in the local name being "reserved" and thus masking the outer-scope name when the function is called.
因此,局部变量似乎是在函数创建时定义的(成功),导致局部名称被“保留”,从而在调用函数时屏蔽了外部作用域名称。
Interesting.
有趣的。
Thanks Frédéric for the answer(s)!
感谢 Frédéric 的回答!
For reference, from the python docs:
作为参考,来自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.)
重要的是要认识到作用域是通过文本确定的:模块中定义的函数的全局作用域是该模块的命名空间,无论从何处调用函数或通过什么别名调用该函数。另一方面,实际的名称搜索是在运行时动态完成的——然而,语言定义正在向静态名称解析发展,在“编译”时,所以不要依赖动态名称解析!(实际上,局部变量已经是静态确定的。)
Edit 4
编辑 4
The Real Answer
真正的答案
This seemingly confusing behaviour is caused by Python's statically nested scopes as defined in PEP 227. It actually has nothing to do with PEP 3104.
这种看似令人困惑的行为是由PEP 227 中定义的 Python静态嵌套作用域引起的。它实际上与PEP 3104无关。
From PEP 227:
来自 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:
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.
名称解析规则对于静态范围语言是典型的[...] [除外] 变量未声明。如果名称绑定操作发生在函数中的任何位置,则该名称将被视为函数的本地名称,并且所有引用都引用本地绑定。如果在绑定名称之前发生引用,则会引发 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。
Lets run two simpler versions of Tim's example:
让我们运行 Tim 示例的两个更简单版本:
>>> i = 6
>>> def f(x):
... def g():
... print i
... # ...
... # later
... # ...
... i = x
... g()
...
>>> f(3)
3
when g()doesn't find iin its inner scope, it dynamically searches outwards, finding the iin f's scope, which has been bound to 3through the i = xassignment.
当在其内部作用域中g()没有找到i时,它会动态向外搜索,找到通过赋值绑定到的iinf作用域。3i = x
But changing the order the final two statements in fcauses an error:
但是更改最后两个语句的顺序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
Remembering that PEP 227 said "The name resolution rules are typical for statically scoped languages", lets look at the (semi-)equivalent C version offer:
记住 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);
}
compile and run:
编译并运行:
$ gcc nested.c -o nested
$ ./nested
134520820
3
So while C will happily use an unbound variable (using whatever happens to have been stored there before: 134520820, in this case), Python (thankfully) refuses.
因此,虽然 C 很乐意使用一个未绑定的变量(使用之前存储在那里的任何东西:134520820,在这种情况下),Python(谢天谢地)拒绝了。
As an interesting side-note, statically nested scopes enable what Alex Martelli has called"the single most important optimization the Python compiler does: a function's local variables are not kept in a dict, they're in a tight vector of values, and each local variable access uses the index in that vector, not a name lookup."
作为一个有趣的旁注,静态嵌套作用域实现了Alex Martelli 所说的“Python 编译器所做的最重要的优化:函数的局部变量不保存在 dict 中,它们在一个紧密的值向量中,并且每个局部变量访问使用该向量中的索引,而不是名称查找。”
采纳答案by Frédéric Hamidi
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.
这是 Python 名称解析规则的产物:您只能访问全局和局部作用域,但不能访问其间的作用域,例如不能访问您的直接外部作用域。
EDIT:The above was poorly worded, you dohave access to the variables defined in outer scopes, but by doing x = xor mymethod = mymethodfrom a non-global namespace, you're actually masking the outer variable with the one you're defining locally.
编辑:以上措辞不好,您确实可以访问在外部作用域中定义的变量,但是通过执行x = x或mymethod = mymethod从非全局命名空间,您实际上是用您在本地定义的变量来屏蔽外部变量。
In example 2, your immediate outer scope is the global scope, so MyClasscan see mymethod, but in example 4 your immediate outer scope is my_defining_func(), so it can't, because the outer definition of mymethodis already masked by its local definition.
在示例 2 中,您的直接外部作用域是全局作用域,因此MyClass可以看到mymethod,但在示例 4 中,您的直接外部作用域是my_defining_func(),因此不能,因为 的外部定义mymethod已经被其本地定义屏蔽了。
See PEP 3104for more details about nonlocal name resolution.
有关非本地名称解析的更多详细信息,请参阅PEP 3104。
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:
另请注意,由于上述原因,我无法让示例 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
But the following would work:
但以下方法可行:
>>> def myfunc():
... x = 3
... class MyClass(object):
... y = x
... return MyClass
...
>>> myfunc().y
3
回答by user3022222
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.
这篇文章已经有几年了,但它是讨论 Python 中作用域和静态绑定的重要问题的罕见文章之一。但是,作者对示例 3 存在一个重要的误解,可能会使读者感到困惑。(不要想当然地认为其他的都正确,只是我只是详细地看了例子3提出的问题)。让我澄清一下发生了什么。
In example 3
在示例 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 xwas assigned to 3in the global scope. Thus a wrong understanding of what happened.
必须返回一个错误,不像帖子的作者所说的。我相信他错过了错误,因为在示例 1 中x被分配到3全局范围内。因此对发生的事情的错误理解。
The explanation is extensively described in this post How references to variables are resolved in Python
这篇文章详细描述了解释 如何在 Python 中解析对变量的引用

