我试图理解调用python函数的过程,我创建了调用.pyc
的简单os.listdir('.')
文件,
我看到os
和listdir
保存在co_names
表中,执行CALL_FUNCTION
字节码指令时,如何识别os
库?它的名字是使用co_names
表吗?是python开始搜索名为os.pyc
的模块?如果是这样,python如何知道.pyc
模块中调用的函数的字节码的偏移量在哪里?
感谢。
dis module bytecode snippet
5 28 LOAD_NAME 0 (os)
31 LOAD_ATTR 2 (listdir)
34 LOAD_CONST 3 ('.')
37 CALL_FUNCTION 1
答案 0 :(得分:4)
Python的虚拟机是基于堆栈的。对Python对象的引用被压入堆栈,并且操作码将其中的一个或多个关闭,执行某些操作,并且通常将结果推回堆栈以供下一个操作码使用。
顺便说一句,你可能会发现一个简单的算术计算很有趣(操作必须完全重新排序才能以这种格式工作)。或者阅读FORTH; Python的VM与FORTH没有什么不同,但实际的FORTH 语言以一种Python的方式反映了它的VM。无论如何,关于解释......
LOAD_NAME
操作码获取对os
对象的引用。 (它恰好是一个模块,但它与什么类型的对象无关,它对所有类型的对象都有效。)引用放在堆栈的顶部。
(这不会搜索或加载模块.Python已经使用先前的os
语句导入了对import
的引用,并且只是从全局变量中检索此引用。)< / p>
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
表中。