我有一个python文件:
$ cat main.py
def foo():
x = 10
y = 20
return x+y
foo()
python的反汇编如下:
$ python -m dis main.py
1 0 LOAD_CONST 0 (<code object foo at 0x109a933b0, file "main.py", line 1>)
3 MAKE_FUNCTION 0
6 STORE_NAME 0 (foo)
6 9 LOAD_NAME 0 (foo)
12 CALL_FUNCTION 0
15 POP_TOP
16 LOAD_CONST 1 (None)
19 RETURN_VALUE
这是我的问题:
1)在反汇编中,1
指main.py
中的行号,LOAD_CONST
指可读格式的操作码。 0
之后的(foo)
的大部分是什么?
2)看来python加载了函数名foo
,然后调用了操作码CALL_FUNCTION
。 LOAD_CONST
-> STORE_NAME
在堆栈/堆中发生了什么。 python在后台保留什么记录(请尽可能低级别,欢迎指向CPython源代码)?
我在https://docs.python.org/2/library/dis.html上看到STORE_NAME
:
STORE_NAME(namei)
实现
name = TOS
。 namei是name中的索引 代码对象的属性co_names。编译器尝试使用 STORE_FAST或STORE_GLOBAL(如果可能)。
但是,除了我们正在做类似foo = Top Of Stack
的事情之外,这对我来说还不清楚。那么foo
实际上就是co_name
中co_name[namei]
中的东西。如果我们进入LOAD_NAME
,它将把co_name
的东西带到TOS
上。什么是co_name
(已分配堆unordered_map
,dict
)?
我觉得自己正在钻研越来越深的漏洞,我认为我应该退后一步,看看社区可以提供什么,然后再继续。
更新:现在我想到了,因为我们正在做类似co_name[namei]
的事情,并且如果namei
是and index。 co_name
仅仅是一个内存管理的数组吗?
答案 0 :(得分:1)
Jason harper的评论解释了您的前两个问题。但是,换句话说,操作码名称后面的数字是它们的参数,括号中的内容指示这些参数实际指的是什么。事实证明,大多数操作码都接受一个参数,对于您的特殊情况,所有索引都索引为0。
关于您的另一个问题,如果可以的话,LOAD_NAME实际上就是LOAD_BY_NAME。它将函数foo
推送到TOS上,以便CALL_FUNCTION知道要调用的内容。
Python将名称存储在名为co_names
的元组中,并进一步维护locals
字典,也称为f_locals
(在Python中可通过locals()
访问)。 LOAD_NAME在co_names
中的特定索引处获取名称,然后在locals
中查找名称。常量直接存储在另一个元组中,并进行数字索引。
关于代码参考,请参考CPython发行版中的$PYPATH/Python/ceval.c
,或直接在GitHub上进行检查。