在函数内创建类并访问包含函数范围内定义的函数 [英] Creating a class within a function and access a function defined in the containing function's scope

查看:71
本文介绍了在函数内创建类并访问包含函数范围内定义的函数的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

修改:

在此问题的底部查看我的完整答案.

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名称解析规则的产物:您只能访问全局范围和局部范围,而不能访问它们之间的范围,例如不在您的直接外部范围内.

上面的措辞很差,您可以访问外部作用域中定义的变量,但是可以通过非作用域中的x = xmymethod = mymethod -global命名空间,实际上是用您在本地定义的变量掩盖了外部变量.

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

有关非本地名称解析的详细信息,请参见 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

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.

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

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

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.

>>> 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().

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.

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

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:

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

Edit 2

Example 5.

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

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:

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

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

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.

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:

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.

Lets run two simpler versions of Tim's example:

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

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

But changing the order the final two statements in 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

Remembering that PEP 227 said "The name resolution rules are typical for statically scoped languages", lets look at the (semi-)equivalent C version offer:

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

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

这篇关于在函数内创建类并访问包含函数范围内定义的函数的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!

查看全文
登录 关闭
扫码关注1秒登录
发送“验证码”获取 | 15天全站免登陆