在函数内创建类并访问包含函数范围内定义的函数 [英] Creating a class within a function and access a function defined in the containing function's scope
问题描述
修改:
在此问题的底部查看我的完整答案.
tl; dr答案:Python具有静态嵌套的作用域. 静态 方面可以与隐式变量声明进行交互,从而产生非显而易见的结果.
(由于该语言通常具有动态特性,所以这尤其令人惊讶).
我以为我对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()
,该范围不是全局的.
正如下面指出的 @ user3022222 ,我在我的示例中弄错了这个例子.原始过帐.我相信这会失败,因为只有函数(其他代码块(如此类定义)无法访问封闭范围内的变量).对于非功能代码块,只能访问局部变量,全局变量和内置变量. 此问题
一个:
示例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有何不同?
我完全迷住了.请把我整理一下. 谢谢!
P.S.偶然地,这不仅仅是我的理解上的问题,我已经在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)的答案!
作为参考,来自 Python文档:
重要的是要认识到范围 由文字决定:全球 在一个函数中定义的功能范围 module是该模块的名称空间,否 从何处或通过什么别名的问题 函数被调用.另一方面, 实际搜索姓名已完成 在运行时动态地-但是, 语言定义在不断发展 朝向静态名称解析 编译"时间,所以不要依赖 动态名称解析! (实际上, 局部变量已经确定 静态地.)
编辑4
真实答案
这种看似令人困惑的行为是由Python的静态嵌套作用域(如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
时,它将动态向外搜索,在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(谢天谢地)拒绝了.
作为一个有趣的旁注,静态嵌套范围启用了 这是Python名称解析规则的产物: 上面的措辞很差,您可以访问外部作用域中定义的变量,但是可以通过非作用域中的 在示例2中,您的直接外部作用域是全局作用域,因此 有关非本地名称解析的详细信息,请参见 PEP 3104 . 还要注意,由于上述原因,我无法让示例3在Python 2.6.5或3.1.2下运行: 但是以下方法会起作用: 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). 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 ;) 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. Example 1. Straightforward enough: during class definition we're able to access the variables defined in the outer (in this case global) scope. Example 2. Again (ignoring for the moment why one 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. Example 3. 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 One more: Example 4. Um...excuse me? What makes this any different from example 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. Edit
According 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: #4. seems to be a counter-example to the second of these. Edit 2 Example 5. Edit 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. So this modified version of Example 4 works: However this doesn't: 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): 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)! For reference, from the python docs: 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 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. From 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: 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. Lets run two simpler versions of Tim's example: when But changing the order the final two statements in Remembering that PEP 227 said "The name resolution rules are typical for statically scoped languages", lets look at the (semi-)equivalent C version offer: compile and run: 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. 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." That's an artifact of Python's name resolution rules: EDIT: The above was poorly worded, you do have access to the variables defined in outer scopes, but by doing In example 2, your immediate outer scope is the global scope, so 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: But the following would work:
这篇关于在函数内创建类并访问包含函数范围内定义的函数的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!您只能访问全局范围和局部范围,而不能访问它们之间的范围,例如不在您的直接外部范围内. x = x
或mymethod = mymethod
-global命名空间,实际上是用您在本地定义的变量掩盖了外部变量.MyClass
可以看到mymethod
,但是在示例4中,您的直接外部作用域是my_defining_func()
,所以不能,因为它的外部定义mymethod
已被其本地定义掩盖.>>> 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
>>> x = 3
>>> class MyClass(object):
... x = x
...
>>> MyClass.x
3
>>> def mymethod(self):
... return self.x
...
>>> x = 3
>>> class MyClass(object):
... x = x
... mymethod = mymethod
...
>>> MyClass().mymethod()
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()
.>>> 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
>>> def fun1():
... x = 3
... def fun2():
... print x
... return fun2
...
>>> fun1()()
3
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>
The Real Answer
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()
>>> i = 6
>>> def f(x):
... def g():
... print i
... # ...
... # later
... # ...
... i = x
... g()
...
>>> f(3)
3
g()
doesn't find i
in its inner scope, it dynamically searches outwards, finding the i
in f
's scope, which has been bound to 3
through the i = x
assignment.f
causes an error:>>> 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
// 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
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.x = x
or mymethod = mymethod
from a non-global namespace, you're actually masking the outer variable with the one you're defining locally.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.>>> 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