python - 如果 Python 源代码在解释/JITing 之前编译为字节码,为什么在运行之前没有捕获到这个错误?
问题描述
我写了以下函数:
def f():
for i in range(100000):
print(i)
some_function_that_doesnt_exist()
当我运行我的文件时,这将打印出 100000 范围内的数字,然后引发错误:NameError: name 'some_function_that_doesnt_exist' is not defined
.
这是我对 Python 提出的更广泛问题的一个示例。如果 Python 代码在 Python VM 解释它(或 JIT)之前被编译为字节码,为什么我们在编译过程中看不到这个错误?为什么在我们最终看到错误之前打印出 100000 个数字?在像 C 这样的纯编译语言中,我们会在编译时看到错误,那么为什么我们不在这种“部分编译”的语言中看到它呢?
解决方案
您可以使用以下命令查看 python VM 运行的内容dis
:
import dis
def f():
for i in range(100000):
print(i)
some_function_that_doesnt_exist()
dis.dis(f)
将输出:
4 0 SETUP_LOOP 24 (to 26)
2 LOAD_GLOBAL 0 (range)
4 LOAD_CONST 1 (100000)
6 CALL_FUNCTION 1
8 GET_ITER
>> 10 FOR_ITER 12 (to 24)
12 STORE_FAST 0 (i)
5 14 LOAD_GLOBAL 1 (print)
16 LOAD_FAST 0 (i)
18 CALL_FUNCTION 1
20 POP_TOP
22 JUMP_ABSOLUTE 10
>> 24 POP_BLOCK
6 >> 26 LOAD_GLOBAL 2 (some_function_that_doesnt_exist)
28 CALL_FUNCTION 0
30 POP_TOP
32 LOAD_CONST 0 (None)
34 RETURN_VALUE
这就是 python VM 实际运行的内容。从第 10 行到第 24 行可以看出,它实际上处理的是打印。
只有当您到达第 26 行时,python VM 才会尝试加载该函数,这会导致名称错误。
有点不同的是,当编译语言编译为字节码(例如 C 到汇编)时,函数调用需要确切地知道函数在内存中的位置。因此,当您尝试编译一个不存在的函数时,它将无法将其转换为字节码,因为它无法查找函数位置。
Python VM可以访问一个全局数组,可以实时修改,在这个数组中查找也是实时的。
正在做:
dis.dis("def some_function_that_doesnt_exist(): 1")
将导致:
1 0 LOAD_CONST 0 (<code object some_function_that_doesnt_exist at 0x7f38dc90ed20, file "<dis>", line 1>)
2 LOAD_CONST 1 ('some_function_that_doesnt_exist')
4 MAKE_FUNCTION 0
6 STORE_NAME 0 (some_function_that_doesnt_exist)
8 LOAD_CONST 2 (None)
10 RETURN_VALUE
其中第 2 行从 const 表中加载函数名,在第 4 行创建函数后,将其根据名称存储在第 6 行。这样下次查找该函数时,python VM 将能够找到它.
推荐阅读
- python - 如何在列表推导中进行扩展
- python - Django CookieCutter:django.db.utils.OperationalError:fe_sendauth:未提供密码
- javascript - 需要的建议:是否可以在一天中的某个时间更改文件路径以显示不同的内容?
- javascript - Discord.js 导入意图
- android - Android可以访问android中任何应用程序的应用内购买吗
- html - How to remove space between content and left border in p tag?
- ruby-on-rails - 优化/缩短此代码片段的方法
- html - 下标和常规文本 HTML 之间的水平空格
- sql - PL/SQL 获取多于请求的行数
- firebase - 如何将 Firebase 集合的每个文档设为 ListTile?