第一个信息:常量1在python内部,它是个对象,这个对象的类型是PyLong_Type型,这是Python的整型内部实现 另外,该对象的引用数是 126 个,说明这个常量对象其实是被共享的,LOAD_CONST 指令会让它的引用数加 1。我们用的常数是 1,这个值在 Python 内部也是会经常被用到,所以引用数会这么高。 进一步发现,往栈里放...
读取第三条字节码指令,操作码等于 100,此时是 LOAD_CONST 这条指令,那么此时的操作码等于 arg += 65,因为操作码不是 EXTENDED_ARG 因此操作数不需要在乘以 256 了。 上面的计算过程用程序代码表示如下,下面的代码当中 code 就是真正的字节序列 HAVE_ARGUMENT = 90 。 def _unpack_opargs(code): extended_arg...
为了LOAD_CONST,有2个空洞需要填补,即oparg和下一条指令:static const Hole _LOAD_CONST_code_holes[3] = {{0xd, HoleKind_X86_64_RELOC_UNSIGNED, HoleValue_OPARG, NULL, 0x0},{0x46, HoleKind_X86_64_RELOC_UNSIGNED, HoleValue_CONTINUE, NULL, 0x0},};然后,所有机器代码都作为字节序列存储在...
2、LOAD_CONST 1:带入 co_consts 在索引 1 上的字面值,并将它推入(索引 0 上的字面值是 None,它表示在 co_consts 中,因为 Python 函数调用有一个隐式的返回值 None,如果没有显式的返回表达式,就返回这个隐式的值 )。 3、CALL_FUNCTION 1:告诉 Python 去调用一个函数;它需要从栈中弹出一个位置参数,然...
可以看到,LOAD_CONST(1),对应了co_consts[1],就是100;LOAD_CONST(2),对应了一个字符串,而LOAD_CONST(0),恰好就是None。可以看到,与dis的输出的对应行的最后面的括号里的东西完全对应。 LOAD_NAME LOAD_NAME(namei)加载一个名为co_names[namei]的东西,看上去有一个搜索顺序,可以看ceval.c里的实现。 可...
7 22 LOAD_GLOBAL 3 (inspect) 24 LOAD_ATTR 4 (currentframe) 26 CALL_FUNCTION 0 28 STORE_GLOBAL 5 (y) 8 30 LOAD_CONST 2 (2) 32 STORE_FAST 3 (f) 34 LOAD_CONST 0 (None) 36 RETURN_VALUE 1. 2. 3. 4. 5. 6. 7. 8.
“a,b=b,a”操作:两个 LOAD_FAST 是从局部作用域中读取变量的引用,并存入栈中,接着是最关键的 ROT_TWO 操作,它会交换两个变量的引用值,然后两个 STORE_FAST 是将栈中的变量写入局部作用域中。 “a,b=1,2”操作:第一步 LOAD_CONST 把“=”号右侧的两个数字作为元组放到栈中,第二步 UNPACK_SEQUENCE...
当从源代码编译CPython时,可以提供一个标志--enable-experimental-jit到配置脚本。这将为Python字节码生成机器代码模板。这是通过首先复制每个字节码的C代码来实现的,例如最简单的LOAD_CONST: frame->instr_ptr = next_instr;next_instr += 1;INSTRUCTION_STATS(LOAD_CONST);PyObject *value;value = GETITEM(FRAME...
8 LOAD_NAME 0 (foo): 注意这一步是在调用的时候发生的, 将变量foo加载进来 10 CALL_FUNCTION 0: 通过CALL_FUNCTION指令调用该函数(我们后面将要分析的重点), 后面的0表示参数个数 12 POP_TOP: 将上一步函数的返回值从运行时栈的顶部弹出 14 LOAD_CONST 2 (None): 加载返回值None ...
以加法为例,第一步是LOAD_GLOBAL(加载全局变量n),第二步LOAD_CONST(加载常量1),第三步进行二进制的加法,第四步将计算结果存储到全局变量n中,加法计算结束。这四个指令如果能够保证被作为一个整体完整地运行,那么是不会产生问题的,但根据前面说的线程释放GIL的原则,那么很有可能在线程正在执行这四步中的任何一...