名称*open*是否属于此示例中的内置或全局范围?

yua*_*210 2 python lookup scope global built-in

请考虑以下代码段:

global open
print(open)
Run Code Online (Sandbox Code Playgroud)

得出以下结果:

<built-in function open>
Run Code Online (Sandbox Code Playgroud)

我的问题是:在这个例子中,名称open是否属于内置或全局范围?

我认为全局声明会强制将name 打开映射到全局范围(因此,会导致我们出错),这不会发生在这里.为什么?

aba*_*ert 5

一,直接回答:

该名称open属于顶级命名空间.这本质上意味着"查找全局变量,回退到内置函数;分配给全局变量".

添加global open只是强制它属于顶级命名空间,它已经存在.(我假设这是顶级代码,而不是函数或类.)

这似乎与你读到的内容有关吗?嗯,这有点复杂.


根据参考文档:

global语句是一个声明,它适用于整个当前代码块.这意味着列出的标识符将被解释为全局变量.


但是,尽管文档的其他部分似乎暗示,"解释为全局"实际上并不意味着"在全局命名空间中搜索",而是"在顶级命名空间中搜索",如名称解析中所述:

通过搜索全局命名空间(即包含代码块的模块的命名空间)和内置命名空间(模块的命名空间),在顶级命名空间中解析名称builtins.首先搜索全局命名空间.如果在那里找不到builtins名称,则搜索名称空间.

并且"作为全局变量"意味着"与查找全局名称空间中的名称的方式相同",即"在顶级名称空间中".

当然,对顶级命名空间的赋值总是归于全局变量,而不是内置变量.(这就是为什么你可以首先open用全局影响内置的open.)


另外,请注意,正如在execeval文档中所解释的那样,对于运行的代码来说,即使这样也不是这样exec:

如果全局字典不包含键的值,则在该键下插入__builtins__对内置模块的字典的引用builtins.这样,您可以通过__builtins__在传递给自己的字典之前将自己的字典插入到globals中来控制已执行代码可用的内置函数exec().

并且exec最终是如何执行模块和脚本的.

所以,真正发生的事情 - 至少在默认情况下 - 是搜索全局命名空间; 如果找不到该名称,则在全局名称空间中搜索一个__builtins__值; 如果那是模块或映射,则搜索它.


如果你很好奇CPython的工作原理:

  • 在编译时:
    • 编译器为函数构建一个符号表,将名称分隔为freevars(nonlocals),cellvars(由嵌套函数用作nonlocals的locals),locals(任何其他locals)和globals(当然这在技术上意味着"顶级"命名空间"变量".这是global语句发挥作用的地方:它强制将名称添加到全局符号表而不是另一个.
    • 然后它编译代码,并发出LOAD_GLOBAL全局变量的指令.(它将各种名称存储在代码对象的元组成员中,如co_names全局变量和单元co_cellvars格等等.)
  • 在运行时:
    • 当从编译的代码创建函数对象时,__globals__它将作为属性附加到它.
    • 当一个函数被调用时,它就__globals__变成f_globals了框架.
    • 解释器的eval循环然后LOAD_GLOBAL通过完全按照您的期望来处理每条指令f_globals,包括文档中__builtins__描述的回退exec.