我试图理解调用 python 函数的过程,我创建了简单的 .pyc
文件来调用 os.listdir('.')
,
我看到 os
和 listdir
保存在 co_names
表中,当执行 CALL_FUNCTION
字节码指令时,如何 os
库正在被识别?它是使用 co_names
表的名称吗? python 是否开始搜索名为 os.pyc
的模块?如果是这样,python 如何知道 .pyc
模块中调用的函数的字节码偏移量在哪里?
谢谢。
dis 模块字节码片段
5 28 LOAD_NAME 0 (os)
31 LOAD_ATTR 2 (listdir)
34 LOAD_CONST 3 ('.')
37 CALL_FUNCTION 1
最佳答案
Python 的虚拟机是基于堆栈的。对 Python 对象的引用被压入堆栈,操作码将其中一个或多个拉出,执行一些操作,并且通常将结果推回堆栈以供下一个操作码使用。
顺便说一句,您可能会发现反汇编一个简单的算术计算很有趣(必须完全重新排序运算才能以这种格式工作)。或者继续阅读 FORTH; Python 的 VM 与 FORTH 的并无不同,但实际的 FORTH 语言 以 Python 所没有的方式反射(reflect)了它的 VM。无论如何,继续解释......
LOAD_NAME
操作码获取对 os
对象的引用。 (它恰好是一个模块,但不管它是什么类型的对象,它对所有类型的对象都是一样的。)引用放在堆栈的顶部。
(这不会搜索或加载模块。Python 已经使用先前的 import
语句导入了对 os
的引用,并且只是从中检索此引用全局变量。)
LOAD_ATTR
操作码获取对堆栈顶部引用的任何对象的 listdir
对象的引用。 (同样,此引用是一个函数,但这并不重要。)堆栈顶部的对象引用被弹出,LOAD_ATTR
的结果被压入。
LOAD_CONST
操作码获取对字符串 '.'
的引用并将其压入堆栈顶部。
现在 CALL_FUNCTION
从堆栈中弹出 1 个引用。这是对字符串 '.'
的引用,os.listdir
的参数。 (它知道弹出 1 个引用,因为 CALL_FUNCTION
的操作数是 1。如果函数接受更多参数,就会有更多的 LOAD
操作码和 的操作数CALL_FUNCTION
操作码会更高。)它从堆栈中弹出另一个引用,这是对 os.listdir
函数的引用。然后它使用参数调用函数。然后将函数的返回值压入堆栈,供其他操作码使用。
正如您所发现的,名称 os
和 listdir
存储在表 co_names
中。 LOAD_NAME
和 LOAD_ATTR
操作码的操作数是该表的索引。 '.'
的处理方式类似,只是它存储在 co_consts
表中。
关于python - 调用python库函数的过程,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/35469722/