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

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

问题描述

<块引用>

编辑:

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

tl;dr answer:Python 具有静态嵌套的作用域.静态aspect 可以与隐式变量声明交互,产生不明显的结果.

(这可能特别令人惊讶,因为该语言通常具有动态特性).

我认为我对 Python 的范围规则有很好的处理,但这个问题让我彻底陷入困境,我的 google-fu 让我失望了(我并不感到惊讶 - 看看问题标题;)

我将从几个按预期工作的示例开始,但请随意跳到示例 4 了解更多内容.

示例 1.

>>>x = 3>>>我的类(对象):... x = x...>>>我的类.x3

直截了当:在类定义期间,我们能够访问在外部(在本例中为全局)范围中定义的变量.

示例 2.

>>>定义我的方法(自我):...返回self.x...>>>x = 3>>>我的类(对象):... x = x... 我的方法 = 我的方法...>>>MyClass().mymethod()3

再一次(暂时忽略为什么人们可能想要这样做),这里没有什么意外的:我们可以访问外部范围内的函数.

注意:正如 Frédéric 在下面指出的,此功能似乎不起作用.请参阅示例 5(及以后).

示例 3.

>>>定义我的函数():... x = 3...类MyClass(对象):... x = x...返回我的班级...>>>myfunc().x回溯(最近一次通话最后):<module> 中的文件<stdin>"第 1 行myfunc 中的文件<stdin>",第 3 行MyClass 中的文件<stdin>"第 4 行NameError:名称x"未定义

这与示例 1 基本相同:我们从类定义中访问外部作用域,只是这一次该作用域不是全局的,这要感谢 myfunc().

编辑 5:正如 @user3022222 在下面指出的,我在我的原帖.我相信这会失败,因为只有函数(而不是其他代码块,如此类定义)可以访问封闭范围内的变量.对于非功能代码块,只能访问局部变量、全局变量和内置变量.这个问题

还有一个:

示例 4.

>>>def my_defining_func():...定义我的方法(自我):...返回self.y...类MyClass(对象):... 我的方法 = 我的方法... y = 3...返回我的班级...>>>我的定义函数()回溯(最近一次通话最后):<module> 中的文件<stdin>"第 1 行文件<stdin>",第 4 行,在 my_defining_func 中MyClass 中的文件<stdin>",第 5 行NameError:名称mymethod"未定义

嗯...打扰一下?

这与示例 2 有何不同?

我完全糊涂了.请给我整理一下.谢谢!

附:如果这不仅仅是我理解的问题,我已经在 Python 2.5.2 和 Python 2.6.2 上尝试过.不幸的是,这些是我目前可以访问的所有内容,但它们都表现出相同的行为.

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

  • 最里面的范围,即首先搜索,包含本地名字
  • 任何封闭的范围搜索到的函数从最近的封闭开始范围,包含非本地的,但也非全局名称
  • 倒数第二个范围包含当前模块的全局名称
  • 最外层范围(最后搜索)是包含内置的命名空间名字

#4.似乎是其中第二个的反例.

编辑 2

示例 5.

>>>定义乐趣1():... x = 3...定义乐趣2():... 打印 x...返回乐趣2...>>>乐趣1()()3

编辑 3

正如@Frédéric 指出的,分配给与外部范围内同名的变量似乎掩盖"了外部变量,从而阻止了分配功能.

所以示例 4 的这个修改版本有效:

def my_defining_func():def mymethod_outer(self):返回self.y我的类(对象):mymethod = mymethod_outery = 3返回我的班级我的定义函数()

但事实并非如此:

def my_defining_func():定义我的方法(自我):返回self.y我的类(对象):mymethod_temp = 我的方法mymethod = mymethod_tempy = 3返回我的班级我的定义函数()

我仍然不完全理解为什么会发生这种屏蔽:分配发生时不应该发生名称绑定吗?

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

>>>def my_defining_func():... x = 3... def my_inner_func():... x = x...返回 x...返回 my_inner_func...>>>my_defining_func()()回溯(最近一次通话最后):<module> 中的文件<stdin>"第 1 行文件<stdin>",第 4 行,在 my_inner_funcUnboundLocalError:分配前引用的局部变量x">>>我的定义函数()<函数 my_inner_func 在 0xb755e6f4>

所以看起来局部变量是在函数创建时定义的(成功),导致局部名称被保留",从而在调用函数时屏蔽了外部范围名称.

有趣.

感谢 Frédéric 的回答!

供参考,来自 python 文档:

<块引用>

认识到范围很重要由文本确定:全球a 中定义的函数的范围module 是该模块的命名空间,否来自哪里或以什么别名函数被调用.另一方面,名称的实际搜索已完成在运行时动态地——然而,语言定义正在演变走向静态名称解析,在编译"时间,所以不要依赖动态名称解析!(实际上,局部变量已经确定静态的.)

编辑 4

真正的答案

这种看似令人困惑的行为是由 Python 的 PEP 227 中定义的静态嵌套范围.它实际上与 PEP 3104 无关.

来自 PEP 227:

<块引用>

名称解析规则是典型的对于静态范围的语言 [...][except] 变量未声明.如果发生名称绑定操作函数中的任何位置,然后是该名称被视为函数的本地所有参考资料均指当地捆绑.如果引用发生在之前名称是绑定的,一个 NameError 是提出来.

[...]

蒂姆·彼得斯 (Tim Peters) 的一个例子展示了没有声明的嵌套范围:

i = 6定义 f(x):定义 g():打印我# ...# 跳到下一页# ...for i in x: # ah, i *is* 是 f 的局部变量,所以这是 g 看到的经过G()

对 g() 的调用将引用由 for 绑定在 f() 中的变量 i环形.如果在循环执行之前调用 g(),NameError 将被抚养.

让我们运行两个更简单的 Tim 示例:

>>>我 = 6>>>定义 f(x):... 定义 g():...打印我... # ......     # 之后... # ...... 我 = x...     G()...>>>f(3)3

g() 在其内部范围内没有找到 i 时,它会动态向外搜索,在 中找到 if的作用域,已经通过i = x赋值绑定到3.

但是更改f中最后两条语句的顺序会导致错误:

>>>我 = 6>>>定义 f(x):... 定义 g():...打印我... # ......     # 之后... # ......     G()... i = x # 注意:我交换了位置...>>>f(3)回溯(最近一次通话最后):<module> 中的文件<stdin>"第 1 行文件<stdin>",第 7 行,在 f文件<stdin>",第 3 行,以 g 为单位NameError:在封闭范围内赋值之前引用了自由变量i"

记住 PEP 227 说过名称解析规则是静态范围语言的典型规则",让我们看看(半)等效的 C 版本提供:

//nested.c#include <stdio.h>诠释 i = 6;无效 f(int x){诠释我;//<--- 隐含在上面的python代码中无效 g(){printf("%d
",i);}G();我 = x;G();}诠释主要(无效){f(3);}

编译运行:

$ gcc nested.c -o 嵌套$ ./嵌套1345208203

因此,尽管 C 会愉快地使用未绑定的变量(使用之前存储的任何变量:134520820,在这种情况下),但 Python(谢天谢地)拒绝了.

作为一个有趣的旁注,静态嵌套范围可以实现 Alex Martelli 称Python 编译器所做的最重要的优化:函数的局部变量不保存在 dict 中,它们位于紧密的值向量中,并且每个局部变量都可以访问使用该向量中的索引,而不是名称查找."

解决方案

这是 Python 名称解析规则的产物:你只能访问全局和局部范围,但不能访问中间的范围,例如不要在你的直接外部范围内.

以上措辞不佳,您确实可以访问外部范围中定义的变量,但是通过执行 x = xmymethod = mymethod 来自非全局命名空间,您实际上是在用您在本地定义的变量掩盖外部变量.

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

有关非本地名称解析的更多详细信息,请参阅 PEP 3104.p>

还要注意,由于上述原因,我无法让示例 3 在 Python 2.6.5 或 3.1.2 下运行:

>>>定义我的函数():... x = 3...类MyClass(对象):... x = x...返回我的班级...>>>myfunc().x回溯(最近一次通话最后):<module> 中的文件<stdin>"第 1 行myfunc 中的文件<stdin>",第 3 行MyClass 中的文件<stdin>"第 4 行NameError:名称x"未定义

但以下方法可行:

>>>定义我的函数():... x = 3...类MyClass(对象):... y = x...返回我的班级...>>>myfunc().y3

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
",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天全站免登陆