调用Python库函数的流程

3
我正在尝试理解调用Python函数的过程,我创建了一个简单的.pyc文件来调用os.listdir('。'), 我看到oslistdir保存在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
1个回答

5
Python的虚拟机是基于堆栈的。Python对象的引用被推送到堆栈上,一个操作码将其中一个或多个弹出,执行一些操作,通常将结果再次推回堆栈以供下一个操作码使用。
顺便说一句,您可能会发现分解简单的算术计算很有趣(这些操作必须完全重新排序才能在此格式中工作)。或者阅读FORTH的相关资料;Python的VM与FORTH的VM相似,但实际的FORTH语言以一种Python不具备的方式反映了其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表中。

“IMPORT_NAME”指令是在查找“os.pyc”文件吗?模块只被其名称所识别?此外,Python如何知道“listdir”函数字节码在“os.pyc”文件中的位置? - Kikapi
不,正如我所解释的,IMPORT_NAME 查找全局变量中的 os。因为先前使用 import 语句导入了 os,所以它在那里。Python 知道 listdir 函数的位置方式是在 os 模块的命名空间中,即该模块的全局变量中。 - kindall
1
从技术上讲,它仅从全局变量中检索此引用,它会检查作用域 https://dev59.com/J3VC5IYBdhLWcg3wcgud ,在这种情况下,它在全局作用域中找到了它。 - Karoly Horvath

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接