在函数中创建类并访问在包含函数的作用域中定义的函数[重复]

2023-11-24

Edit:

请参阅我在这个问题底部的完整答案。

tl;博士回答:Python 具有静态嵌套作用域。这static方面可以与隐式变量声明交互,产生不明显的结果。

(这可能特别令人惊讶,因为该语言通常是动态的)。

我以为我对 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

再次(暂时忽略why有人可能想要这样做),这里没有什么意外的:我们可以访问外部作用域中的函数。

Note:正如 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

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 指出如下,我在原来的帖子中搞砸了这个例子。我相信这会失败,因为只有函数(而不是其他代码块,如此类定义)可以访问封闭范围内的变量。对于非功能代码块,只能访问局部变量、全局变量和内置变量。更全面的解释可以在这个问题

多一个:

例 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 有何不同?

我完全糊涂了。请帮我安排一下。 谢谢!

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

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

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

#4。似乎是第二个的反例。

Edit 2

实施例5.

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

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

因此,局部变量似乎是在函数创建时定义的(成功),导致局部名称被“保留”,从而在调用函数时屏蔽外部范围名称。

有趣的。

感谢弗雷德里克的回答!

作为参考,来自python 文档:

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

Edit 4

真正的答案

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

来自 PEP 227:

名称解析规则是典型的 对于静态范围的语言 [...] [例外] 未声明变量。 如果发生名称绑定操作 函数中的任何位置,然后是该名称 被视为函数的局部函数 所有引用均指本地 捆绑。如果引用发生在之前 名称已绑定,NameError 是 上调。

[...]

蒂姆彼得斯的一个例子展示了潜在的陷阱 没有声明的嵌套范围:

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

when g()没有找到i在其内部范围内,它动态地向外搜索,找到i in f的范围,这已被绑定到3通过i = x任务。

但是改变最后两个语句的顺序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 编译器所做的最重要的优化是:函数的局部变量不保存在字典中,它们位于值的紧密向量中,并且每个局部变量访问都使用该向量中的索引,而不是名称查找。”


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

EDIT:上面说得不好,你do可以访问外部作用域中定义的变量,但是通过这样做x = x or mymethod = mymethod从非全局命名空间中,您实际上是用您在本地定义的变量掩盖了外部变量。

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

See 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
本文内容由网友自发贡献,版权归原作者所有,本站不承担相应法律责任。如您发现有涉嫌抄袭侵权的内容,请联系:hwhale#tublm.com(使用前将#替换为@)

在函数中创建类并访问在包含函数的作用域中定义的函数[重复] 的相关文章

随机推荐

  • 如何在文本文件中搜索字符串?

    我想检查字符串是否在文本文件中 如果是 则执行 X 如果不是 则执行 Y 但是 此代码始终返回True因为某些原因 任何人都可以看到出了什么问题吗 def check datafile file example txt found Fals
  • 将线程本地内存刷新到全局内存意味着什么?

    我知道Java中易失性变量的目的是对此类变量的写入对其他线程立即可见 我还知道同步块的作用之一是将线程局部内存刷新到全局内存 我从未完全理解在这种情况下对 线程本地 内存的引用 我知道仅存在于堆栈上的数据是线程本地的 但是当谈论堆上的对象时
  • CSS 网格包裹

    是否可以在不使用媒体查询的情况下制作 CSS 网格换行 就我而言 我想要将不确定数量的项目放置在网格中 并且希望该网格进行换行 使用 Flexbox 我无法可靠地很好地间隔事物 我也想避免一堆媒体查询 Here s 一些示例代码 grid
  • 使用 ITextRenderer 从包含非拉丁字符的 HTML 生成 PDF 不起作用

    这是我调查的第二天 没有任何结果 至少现在 我可以问一些非常具体的问题 我正在尝试编写一个有效的 HTML 代码 其中包含 PDF 文件中的一些非拉丁字符iText更具体地使用文本渲染器 from 飞碟 我的简短示例 代码首先使用以下值初始
  • 为什么多个 javascript 库将 $ 用于一种或另一种用途

    我见过几个使用 的 javascript 库 无论是 jQuery mootools prototype 等 甚至一些关于 javascript 的书籍也建议使用 作为 document getElementById 的函数替换 这只是随机
  • Python 请求发布文件

    使用 CURL 我可以发布一个文件 例如 CURL X POST d pxeconfig cat boot txt https ip 8443 tftp syslinux 我的文件看起来像 cat boot txt line 1 line
  • 如何在 iOS 6 中为文本添加下划线?

    我正在尝试在标签中的某些文本下划线 但是 我不知道如何获取标签中整个文本的范围 这是我到目前为止所拥有的 NSMutableAttributedString mat self tableLabel attributedText mutabl
  • 哪个 C# 定时器?

    我正在编写一个包含计时器的类 最重要的是 它可能不会在 0 处初始化 它可能已经开始计时 并且该类将包含 Start Pause Resume 和 Stop Complete 方法 我知道我可以使用 C 中的许多计时器 即 System T
  • Mac OS 10.5 上的 Java 1.6 SDK

    适用于 Mac 的 Java 1 6 SDK 已发布吗 我好像找不到 是的 但仅适用于基于 Intel 的 64 位 Mac 即使用 Core 2 双核或单核 或 Xeon 芯片的 Mac 不支持原装Core芯片 也不支持任何PPC芯片 此
  • C# 应用程序退出时会自动释放托管资源吗?

    我完全知道 using 语句是处理的方式IDisposables 请不要在评论中重复此建议 当 C NET 4 5 或更高版本 应用程序关闭时 会发生什么IDisposable哪些没有得到妥善处置 我知道有些有一个用于处理非托管资源的终结器
  • 如何使用 Webkit 在 Ubuntu 11.04 (Natty Narwhal) 上运行 Eclipse SWT 浏览器组件?

    我在 Eclipse RCP 应用程序中使用 SWT 浏览器控件 在 Linux Ubuntu 10 10 上 这取决于安装 xulrunner 1 9 2 的用户 这很好用 然而 在 Ubuntu 11 04 上 我发现它默认随 xulr
  • 如何在两个不同的 Android 应用程序之间共享 SharedPreferences 文件?

    我已经为此苦苦挣扎了一段时间 基本上 我想让两个应用程序 总是安装在一起 共享首选项 其中一个只是在后台运行并需要使用首选项的服务 应该拥有首选项 但只really需要读取它们 另一个应用程序是前端 UI 应用程序 需要能够写入另一个应用程
  • 拖放图像输入文件并在上传前预览[重复]

    这个问题在这里已经有答案了 我想创建一个 div 附加拖放功能 当有人单击它时 他们可以选择他们的图像 我已经编码了一些东西并且它可以 单击 div 并选择您的图像 上传前预览图像 你可以检查我的小提琴 css uploader width
  • Ajax(这个)不工作

    当尝试访问 container 的 box 类时 在 ajax 调用内部使用 this 不起作用 container on click box function event var description if this 0 style w
  • NumPy 索引:使用布尔数组进行广播

    相关这个问题 我通过布尔数组和广播遇到了索引行为 我不明白 我们知道可以使用整数索引和广播对二维 NumPy 数组进行索引 这是在docs a np array 0 1 2 3 4 5 6 7 8 9 10 11 b1 np array F
  • 打字稿 |不可变 |扩展 Immutable.Map 类型的正确方法

    我有一个用打字稿编写的带有不可变包的react redux应用程序 我有一个来自 api 的数据 在存储中我将其打包到 Map 中 在所有应用程序中 它们都用作地图 我创建了一个界面 export interface PaymentMeth
  • iOS:让应用程序像服务一样运行

    在 iOS 中 我如何指示操作系统让我的应用程序保持运行 即使它不再位于前台 Skype Viber Empatica Zenly 还有更多的应用程序可以做到这一点 基本上 iOS 中不存在服务类型应用程序或功能之类的东西 即使是 后台 应
  • 从“int”转换为“size_t”可能会改变结果的符号 - GCC,C

    在我的项目中 我打开了将警告视为错误并使用 pedantic and ansi标签 我正在使用 GCC 编译器 在这个项目中 我必须使用第三方源代码 该源代码有很多警告 由于我将警告视为错误 因此我在修复他们的代码时遇到了困难 大多数警告都
  • 如何在 IE7 中垂直对齐文本而不使用 CSS 'table-cell' 属性?

    我有固定高度的 div 其中包含文本 我希望文本在 div 中间垂直对齐 但问题在于某些文本是单行 而有些文本则分成两行 对于 IE8 Chrome 和 Firefox 使用display table cell and vertical a
  • 在函数中创建类并访问在包含函数的作用域中定义的函数[重复]

    这个问题在这里已经有答案了 Edit 请参阅我在这个问题底部的完整答案 tl 博士回答 Python 具有静态嵌套作用域 这static方面可以与隐式变量声明交互 产生不明显的结果 这可能特别令人惊讶 因为该语言通常是动态的 我以为我对 P