python - 在这个例子中,名称 *open* 是属于内置作用域还是全局作用域?
问题描述
考虑这个代码片段:
global open
print(open)
这给出了以下结果:
<built-in function open>
我的问题是:在这个例子中,名称open是属于内置作用域还是全局作用域?
我认为全局声明将强制将名称open映射到全局范围(因此,将导致我们出错),这不会发生在这里。为什么?
解决方案
首先,直接回答:
该名称open
属于顶级命名空间。这实质上意味着“在全局变量中查找,回退到内置函数;分配给全局变量”。
添加global open
只是强制它属于它已经存在的顶级名称空间。(我假设这是顶级代码,而不是在函数或类中。)
这似乎与您阅读的内容有关吗?嗯,它有点复杂。
根据参考文档:
该
global
语句是适用于整个当前代码块的声明。这意味着列出的标识符将被解释为全局变量。
但是,尽管文档的其他部分似乎暗示了什么,“解释为全局变量”实际上并不意味着“在全局命名空间中搜索”,而是“在顶级命名空间中搜索”,如名称解析中所述:
通过搜索全局命名空间(即包含代码块的模块的命名空间)和内置命名空间(模块的命名空间)来解析顶级命名空间中的名称
builtins
。首先搜索全局命名空间。如果在那里找不到名称,builtins
则搜索名称空间。
而“作为全局变量”的意思是“在全局命名空间中查找名称的方式相同”,也就是“在顶级命名空间中”。
而且,当然,对顶级命名空间的分配始终是全局变量,而不是内置函数。(这就是为什么您可以首先open
使用全局隐藏内置函数open
。)
另外,请注意,正如文档exec
和eval
文档中所解释的,即使代码运行时也不是这样exec
:
如果 globals 字典不包含 key 的值,则在该 key 下插入
__builtins__
对内置模块字典的引用。builtins
这样,您可以通过将自己的__builtins__
字典插入到全局变量中,然后再将其传递给exec()
.
最终,模块和exec
脚本是如何执行的。
所以,真正发生的事情——至少在默认情况下——是搜索全局命名空间;如果未找到该名称,则在全局命名空间中搜索一个__builtins__
值;如果那是一个模块或一个映射,它会被搜索。
如果你很好奇这在 CPython 中是如何工作的,特别是:
- 在编译时:
- 编译器为函数构建一个符号表,将名称分为自由变量(非局部变量)、单元变量(嵌套函数用作非局部变量的局部变量)、局部变量(任何其他局部变量)和全局变量(这在技术上当然意味着“顶级命名空间”变量)。这就是该
global
语句发挥作用的地方:它强制将名称添加到全局符号表中,而不是添加到不同的符号表中。 - 然后它编译代码,并
LOAD_GLOBAL
为全局变量发出指令。(并且它将各种名称存储在代码对象的元组成员中,例如co_names
全局变量和单元变量co_cellvars
等。)
- 编译器为函数构建一个符号表,将名称分为自由变量(非局部变量)、单元变量(嵌套函数用作非局部变量的局部变量)、局部变量(任何其他局部变量)和全局变量(这在技术上当然意味着“顶级命名空间”变量)。这就是该
- 在运行时:
- 当从编译代码创建函数对象时,它会
__globals__
作为属性附加到它上面。 - 当一个函数被调用时,它
__globals__
成为f_globals
框架的。 - 然后,解释器的 eval 循环
LOAD_GLOBAL
通过执行您对 that 的期望来处理每条指令f_globals
,包括文档__builtins__
中描述的回退到。exec
- 当从编译代码创建函数对象时,它会
推荐阅读
- javascript - ES6 模块的依赖注入
- react-native - 使用 npm install react native app,出现以下错误
- vba - VBA Excel中的索引(匹配())与单元格中的公式
- c# - Unity - 如何从 c# 捕获 EventSystem UI 输入事件?
- c - 在 Linux 中使用 1GB 透明大页面
- sql - SQL Server 左外忽略记录
- python - CPLEX 二次目标 CPLEX 错误 1017:不适用于混合整数问题
- angular - 在 FormArray 反应式表单中设置不小于 0 的值
- javascript - Botkit 听到处理程序函数未在松弛通道上回复
- r - 当 ggplot/ggplotly 包含注释或形状时,散点单击到 weblink 被禁用