import disdef add(a, b):result = a + b + 2 + 3return resultdis.dis(add)# dis.opname 是一个列表,它按字节码值的顺序列出了所有的操作名。
print(dis.opname)# dis.opmap 是一个字典,其键是操作名,值是对应的字节码值。
print(dis.opmap)# 列出所有的字节码操作和它们对应的值:
for op, value in dis.opmap.items():print(f"{op}: {value}")
输出结果如下:
4 0 LOAD_FAST 0 (a)2 LOAD_FAST 1 (b)4 BINARY_ADD6 LOAD_CONST 1 (2)8 BINARY_ADD10 LOAD_CONST 2 (3)12 BINARY_ADD14 STORE_FAST 2 (result)5 16 LOAD_FAST 2 (result)18 RETURN_VALUE
['<0>', 'POP_TOP', 'ROT_TWO', 'ROT_THREE', 'DUP_TOP', 'DUP_TOP_TWO', 'ROT_FOUR', '<7>', '<8>', 'NOP', 'UNARY_POSITIVE', 'UNARY_NEGATIVE', 'UNARY_NOT', '<13>', '<14>', 'UNARY_INVERT', 'BINARY_MATRIX_MULTIPLY', 'INPLACE_MATRIX_MULTIPLY', '<18>', 'BINARY_POWER', 'BINARY_MULTIPLY', '<21>', 'BINARY_MODULO', 'BINARY_ADD', 'BINARY_SUBTRACT', 'BINARY_SUBSCR', 'BINARY_FLOOR_DIVIDE', 'BINARY_TRUE_DIVIDE', 'INPLACE_FLOOR_DIVIDE', 'INPLACE_TRUE_DIVIDE', 'GET_LEN', 'MATCH_MAPPING', 'MATCH_SEQUENCE', 'MATCH_KEYS', 'COPY_DICT_WITHOUT_KEYS', '<35>', '<36>', '<37>', '<38>', '<39>', '<40>', '<41>', '<42>', '<43>', '<44>', '<45>', '<46>', '<47>', '<48>', 'WITH_EXCEPT_START', 'GET_AITER', 'GET_ANEXT', 'BEFORE_ASYNC_WITH', '<53>', 'END_ASYNC_FOR', 'INPLACE_ADD', 'INPLACE_SUBTRACT', 'INPLACE_MULTIPLY', '<58>', 'INPLACE_MODULO', 'STORE_SUBSCR', 'DELETE_SUBSCR', 'BINARY_LSHIFT', 'BINARY_RSHIFT', 'BINARY_AND', 'BINARY_XOR', 'BINARY_OR', 'INPLACE_POWER', 'GET_ITER', 'GET_YIELD_FROM_ITER', 'PRINT_EXPR', 'LOAD_BUILD_CLASS', 'YIELD_FROM', 'GET_AWAITABLE', 'LOAD_ASSERTION_ERROR', 'INPLACE_LSHIFT', 'INPLACE_RSHIFT', 'INPLACE_AND', 'INPLACE_XOR', 'INPLACE_OR', '<80>', '<81>', 'LIST_TO_TUPLE', 'RETURN_VALUE', 'IMPORT_STAR', 'SETUP_ANNOTATIONS', 'YIELD_VALUE', 'POP_BLOCK', '<88>', 'POP_EXCEPT', 'STORE_NAME', 'DELETE_NAME', 'UNPACK_SEQUENCE', 'FOR_ITER', 'UNPACK_EX', 'STORE_ATTR', 'DELETE_ATTR', 'STORE_GLOBAL', 'DELETE_GLOBAL', 'ROT_N', 'LOAD_CONST', 'LOAD_NAME', 'BUILD_TUPLE', 'BUILD_LIST', 'BUILD_SET', 'BUILD_MAP', 'LOAD_ATTR', 'COMPARE_OP', 'IMPORT_NAME', 'IMPORT_FROM', 'JUMP_FORWARD', 'JUMP_IF_FALSE_OR_POP', 'JUMP_IF_TRUE_OR_POP', 'JUMP_ABSOLUTE', 'POP_JUMP_IF_FALSE', 'POP_JUMP_IF_TRUE', 'LOAD_GLOBAL', 'IS_OP', 'CONTAINS_OP', 'RERAISE', '<120>', 'JUMP_IF_NOT_EXC_MATCH', 'SETUP_FINALLY', '<123>', 'LOAD_FAST', 'STORE_FAST', 'DELETE_FAST', '<127>', '<128>', 'GEN_START', 'RAISE_VARARGS', 'CALL_FUNCTION', 'MAKE_FUNCTION', 'BUILD_SLICE', '<134>', 'LOAD_CLOSURE', 'LOAD_DEREF', 'STORE_DEREF', 'DELETE_DEREF', '<139>', '<140>', 'CALL_FUNCTION_KW', 'CALL_FUNCTION_EX', 'SETUP_WITH', 'EXTENDED_ARG', 'LIST_APPEND', 'SET_ADD', 'MAP_ADD', 'LOAD_CLASSDEREF', '<149>', '<150>', '<151>', 'MATCH_CLASS', '<153>', 'SETUP_ASYNC_WITH', 'FORMAT_VALUE', 'BUILD_CONST_KEY_MAP', 'BUILD_STRING', '<158>', '<159>', 'LOAD_METHOD', 'CALL_METHOD', 'LIST_EXTEND', 'SET_UPDATE', 'DICT_MERGE', 'DICT_UPDATE', '<166>', '<167>', '<168>', '<169>', '<170>', '<171>', '<172>', '<173>', '<174>', '<175>', '<176>', '<177>', '<178>', '<179>', '<180>', '<181>', '<182>', '<183>', '<184>', '<185>', '<186>', '<187>', '<188>', '<189>', '<190>', '<191>', '<192>', '<193>', '<194>', '<195>', '<196>', '<197>', '<198>', '<199>', '<200>', '<201>', '<202>', '<203>', '<204>', '<205>', '<206>', '<207>', '<208>', '<209>', '<210>', '<211>', '<212>', '<213>', '<214>', '<215>', '<216>', '<217>', '<218>', '<219>', '<220>', '<221>', '<222>', '<223>', '<224>', '<225>', '<226>', '<227>', '<228>', '<229>', '<230>', '<231>', '<232>', '<233>', '<234>', '<235>', '<236>', '<237>', '<238>', '<239>', '<240>', '<241>', '<242>', '<243>', '<244>', '<245>', '<246>', '<247>', '<248>', '<249>', '<250>', '<251>', '<252>', '<253>', '<254>', '<255>']
{'POP_TOP': 1, 'ROT_TWO': 2, 'ROT_THREE': 3, 'DUP_TOP': 4, 'DUP_TOP_TWO': 5, 'ROT_FOUR': 6, 'NOP': 9, 'UNARY_POSITIVE': 10, 'UNARY_NEGATIVE': 11, 'UNARY_NOT': 12, 'UNARY_INVERT': 15, 'BINARY_MATRIX_MULTIPLY': 16, 'INPLACE_MATRIX_MULTIPLY': 17, 'BINARY_POWER': 19, 'BINARY_MULTIPLY': 20, 'BINARY_MODULO': 22, 'BINARY_ADD': 23, 'BINARY_SUBTRACT': 24, 'BINARY_SUBSCR': 25, 'BINARY_FLOOR_DIVIDE': 26, 'BINARY_TRUE_DIVIDE': 27, 'INPLACE_FLOOR_DIVIDE': 28, 'INPLACE_TRUE_DIVIDE': 29, 'GET_LEN': 30, 'MATCH_MAPPING': 31, 'MATCH_SEQUENCE': 32, 'MATCH_KEYS': 33, 'COPY_DICT_WITHOUT_KEYS': 34, 'WITH_EXCEPT_START': 49, 'GET_AITER': 50, 'GET_ANEXT': 51, 'BEFORE_ASYNC_WITH': 52, 'END_ASYNC_FOR': 54, 'INPLACE_ADD': 55, 'INPLACE_SUBTRACT': 56, 'INPLACE_MULTIPLY': 57, 'INPLACE_MODULO': 59, 'STORE_SUBSCR': 60, 'DELETE_SUBSCR': 61, 'BINARY_LSHIFT': 62, 'BINARY_RSHIFT': 63, 'BINARY_AND': 64, 'BINARY_XOR': 65, 'BINARY_OR': 66, 'INPLACE_POWER': 67, 'GET_ITER': 68, 'GET_YIELD_FROM_ITER': 69, 'PRINT_EXPR': 70, 'LOAD_BUILD_CLASS': 71, 'YIELD_FROM': 72, 'GET_AWAITABLE': 73, 'LOAD_ASSERTION_ERROR': 74, 'INPLACE_LSHIFT': 75, 'INPLACE_RSHIFT': 76, 'INPLACE_AND': 77, 'INPLACE_XOR': 78, 'INPLACE_OR': 79, 'LIST_TO_TUPLE': 82, 'RETURN_VALUE': 83, 'IMPORT_STAR': 84, 'SETUP_ANNOTATIONS': 85, 'YIELD_VALUE': 86, 'POP_BLOCK': 87, 'POP_EXCEPT': 89, 'STORE_NAME': 90, 'DELETE_NAME': 91, 'UNPACK_SEQUENCE': 92, 'FOR_ITER': 93, 'UNPACK_EX': 94, 'STORE_ATTR': 95, 'DELETE_ATTR': 96, 'STORE_GLOBAL': 97, 'DELETE_GLOBAL': 98, 'ROT_N': 99, 'LOAD_CONST': 100, 'LOAD_NAME': 101, 'BUILD_TUPLE': 102, 'BUILD_LIST': 103, 'BUILD_SET': 104, 'BUILD_MAP': 105, 'LOAD_ATTR': 106, 'COMPARE_OP': 107, 'IMPORT_NAME': 108, 'IMPORT_FROM': 109, 'JUMP_FORWARD': 110, 'JUMP_IF_FALSE_OR_POP': 111, 'JUMP_IF_TRUE_OR_POP': 112, 'JUMP_ABSOLUTE': 113, 'POP_JUMP_IF_FALSE': 114, 'POP_JUMP_IF_TRUE': 115, 'LOAD_GLOBAL': 116, 'IS_OP': 117, 'CONTAINS_OP': 118, 'RERAISE': 119, 'JUMP_IF_NOT_EXC_MATCH': 121, 'SETUP_FINALLY': 122, 'LOAD_FAST': 124, 'STORE_FAST': 125, 'DELETE_FAST': 126, 'GEN_START': 129, 'RAISE_VARARGS': 130, 'CALL_FUNCTION': 131, 'MAKE_FUNCTION': 132, 'BUILD_SLICE': 133, 'LOAD_CLOSURE': 135, 'LOAD_DEREF': 136, 'STORE_DEREF': 137, 'DELETE_DEREF': 138, 'CALL_FUNCTION_KW': 141, 'CALL_FUNCTION_EX': 142, 'SETUP_WITH': 143, 'EXTENDED_ARG': 144, 'LIST_APPEND': 145, 'SET_ADD': 146, 'MAP_ADD': 147, 'LOAD_CLASSDEREF': 148, 'MATCH_CLASS': 152, 'SETUP_ASYNC_WITH': 154, 'FORMAT_VALUE': 155, 'BUILD_CONST_KEY_MAP': 156, 'BUILD_STRING': 157, 'LOAD_METHOD': 160, 'CALL_METHOD': 161, 'LIST_EXTEND': 162, 'SET_UPDATE': 163, 'DICT_MERGE': 164, 'DICT_UPDATE': 165}
POP_TOP: 1
ROT_TWO: 2
ROT_THREE: 3
DUP_TOP: 4
DUP_TOP_TWO: 5
ROT_FOUR: 6
NOP: 9
UNARY_POSITIVE: 10
UNARY_NEGATIVE: 11
UNARY_NOT: 12
UNARY_INVERT: 15
BINARY_MATRIX_MULTIPLY: 16
INPLACE_MATRIX_MULTIPLY: 17
BINARY_POWER: 19
BINARY_MULTIPLY: 20
BINARY_MODULO: 22
BINARY_ADD: 23
BINARY_SUBTRACT: 24
BINARY_SUBSCR: 25
BINARY_FLOOR_DIVIDE: 26
BINARY_TRUE_DIVIDE: 27
INPLACE_FLOOR_DIVIDE: 28
INPLACE_TRUE_DIVIDE: 29
GET_LEN: 30
MATCH_MAPPING: 31
MATCH_SEQUENCE: 32
MATCH_KEYS: 33
COPY_DICT_WITHOUT_KEYS: 34
WITH_EXCEPT_START: 49
GET_AITER: 50
GET_ANEXT: 51
BEFORE_ASYNC_WITH: 52
END_ASYNC_FOR: 54
INPLACE_ADD: 55
INPLACE_SUBTRACT: 56
INPLACE_MULTIPLY: 57
INPLACE_MODULO: 59
STORE_SUBSCR: 60
DELETE_SUBSCR: 61
BINARY_LSHIFT: 62
BINARY_RSHIFT: 63
BINARY_AND: 64
BINARY_XOR: 65
BINARY_OR: 66
INPLACE_POWER: 67
GET_ITER: 68
GET_YIELD_FROM_ITER: 69
PRINT_EXPR: 70
LOAD_BUILD_CLASS: 71
YIELD_FROM: 72
GET_AWAITABLE: 73
LOAD_ASSERTION_ERROR: 74
INPLACE_LSHIFT: 75
INPLACE_RSHIFT: 76
INPLACE_AND: 77
INPLACE_XOR: 78
INPLACE_OR: 79
LIST_TO_TUPLE: 82
RETURN_VALUE: 83
IMPORT_STAR: 84
SETUP_ANNOTATIONS: 85
YIELD_VALUE: 86
POP_BLOCK: 87
POP_EXCEPT: 89
STORE_NAME: 90
DELETE_NAME: 91
UNPACK_SEQUENCE: 92
FOR_ITER: 93
UNPACK_EX: 94
STORE_ATTR: 95
DELETE_ATTR: 96
STORE_GLOBAL: 97
DELETE_GLOBAL: 98
ROT_N: 99
LOAD_CONST: 100
LOAD_NAME: 101
BUILD_TUPLE: 102
BUILD_LIST: 103
BUILD_SET: 104
BUILD_MAP: 105
LOAD_ATTR: 106
COMPARE_OP: 107
IMPORT_NAME: 108
IMPORT_FROM: 109
JUMP_FORWARD: 110
JUMP_IF_FALSE_OR_POP: 111
JUMP_IF_TRUE_OR_POP: 112
JUMP_ABSOLUTE: 113
POP_JUMP_IF_FALSE: 114
POP_JUMP_IF_TRUE: 115
LOAD_GLOBAL: 116
IS_OP: 117
CONTAINS_OP: 118
RERAISE: 119
JUMP_IF_NOT_EXC_MATCH: 121
SETUP_FINALLY: 122
LOAD_FAST: 124
STORE_FAST: 125
DELETE_FAST: 126
GEN_START: 129
RAISE_VARARGS: 130
CALL_FUNCTION: 131
MAKE_FUNCTION: 132
BUILD_SLICE: 133
LOAD_CLOSURE: 135
LOAD_DEREF: 136
STORE_DEREF: 137
DELETE_DEREF: 138
CALL_FUNCTION_KW: 141
CALL_FUNCTION_EX: 142
SETUP_WITH: 143
EXTENDED_ARG: 144
LIST_APPEND: 145
SET_ADD: 146
MAP_ADD: 147
LOAD_CLASSDEREF: 148
MATCH_CLASS: 152
SETUP_ASYNC_WITH: 154
FORMAT_VALUE: 155
BUILD_CONST_KEY_MAP: 156
BUILD_STRING: 157
LOAD_METHOD: 160
CALL_METHOD: 161
LIST_EXTEND: 162
SET_UPDATE: 163
DICT_MERGE: 164
DICT_UPDATE: 165Process finished with exit code 0
Q1:为什么for op, value in dis.opmap.items():print(f"{op}: {value}")打印出来的没有7和8等等(即为什么会出现索引不连续的现象)?
A1:Python 字节码的指令集并不是连续的,有些数字可能被保留为未来使用,或者在某个时候被用过但后来被弃用。
在 CPython 的发展过程中,一些字节码指令可能会被修改、移除或新增。当一个字节码指令被移除或修改时,开发者们可能会选择不使用其原来的数字编码,以避免与旧版本的字节码混淆。因此,有些数字编码可能会被跳过,导致在 dis.opmap
中出现不连续的现象。
例如,编号 7
和 8
可能在某个版本的 CPython 中用于某些指令,但后来这些指令可能被移除或替换。为了保持字节码的稳定性,新的指令可能会使用新的数字编码,而旧的编码则被保留并不再使用。
总之,这种不连续性是由于字节码指令集的历史和开发过程造成的。在设计和维护字节码指令集时,开发者们会权衡稳定性、向后兼容性和清晰性等因素。
Q2:dis.opmap输出操作名以及对应的字节码值是什么意思,为什么要将操作名对应字节码值?
A2:操作名和其对应的字节码值的关系可以类比为汇编语言中的助记符(mnemonic)和其对应的机器码。在机器级别,所有的指令都由数字(通常是二进制)表示,但为了人类的可读性和编写的便利,我们使用助记符或高级语言来编写代码。当编译或解释这些代码时,它们被转化为对应的机器码或字节码。
在 Python 字节码的上下文中:
-
操作名(例如:
BINARY_ADD
、LOAD_FAST
) 是指令的助记符。它们是描述性的名称,用于表示字节码的操作。 -
字节码值 是每个操作对应的实际数值表示。它是一个整数,代表了这个操作在字节码序列中的编码。例如,
BINARY_ADD
可能对应于整数23
。
这种映射的存在有几个原因:
-
可读性:操作名为人类提供了一个清晰、描述性的标签来识别各种字节码操作,而不是记住每个操作的数字编码。
-
抽象:使用操作名可以轻松地在不同版本的 Python 或不同的实现(如 CPython、PyPy)之间迁移,即使字节码值有所变化。操作名为我们提供了一种不依赖于特定编码的方式来思考和描述操作。
-
工具和调试:有了操作名和其对应的字节码值,开发者和工具可以轻松地进行字节码分析、调试和优化,而不需要深入到每个字节的级别。
总之,将操作名映射到字节码值提供了一种高级别、可读的方式来处理、理解和操作字节码,同时为底层实现提供了灵活性。
Q3:dis.dis(add)的反汇编结果,为什么变量索引从0开始,常量索引从1开始?
A3:在 Python 的字节码中,LOAD_FAST
操作用于加载函数内部的局部变量,而 LOAD_CONST
用于加载常量。这两者使用的索引方式略有不同,原因如下:
-
局部变量索引(
LOAD_FAST
):-
当函数被编译为字节码时,局部变量(包括函数参数)会被放入一个局部变量数组中。索引
0
,1
,2
, … 对应于该数组中的位置。 -
在你提供的例子中,函数参数
a
和b
分别占据了索引0
和1
。result
是在函数体内部定义的局部变量,所以它被分配了索引2
。
-
-
常量索引(
LOAD_CONST
):-
当函数被编译时,所有在函数中用到的常量都会被放入一个常量元组中。这个元组是按照常量首次出现的顺序进行排列的。
-
但有一个特点:这个常量元组的第一个元素通常是
None
。所以实际上,元组的内容可能是(None, 2, 3, ...)
。因此,常量2
有索引1
,常量3
有索引2
。这里的
None
是为了处理那些不返回任何值的函数。在 Python 中,如果函数没有显式的return
语句,它将返回None
。这样的处理方式确保了每个函数都有一个返回值,即使它是隐式的None
。
-
总的来说,这两种不同的索引策略都是基于 Python 的内部实现决策。局部变量从 0
开始是自然的,因为它们在函数内部首次出现时就被分配了。常量元组从 1
开始的原因则是因为索引 0
通常被 None
占据。
既然如此,那我将这个程序稍微改动一下
import disdef add(a, b):result = 2 + 3return resultdis.dis(add)
输出结果为:
4 0 LOAD_CONST 1 (5)2 STORE_FAST 2 (result)5 4 LOAD_FAST 2 (result)6 RETURN_VALUE
Q4:反汇编的结果中为什么result变量的索引是2呢?不应该是0吗?
A4:在上述例子中,函数 add
有两个参数:a
和 b
。在字节码层面,函数的参数也被视为局部变量,并被赋予局部变量索引。
在函数的局部变量表中,参数会首先被分配索引,从 0
开始。然后函数体内部定义的局部变量会继续被分配索引。
所以,在上述函数中:
- 参数
a
被分配了索引0
- 参数
b
被分配了索引1
- 局部变量
result
被分配了索引2
这就是为什么 result
的索引是 2
,而不是 0
。
Q5:那为什么反汇编出来没看到2、3呢,怎么直接冒出来了个5?
A5:在上述函数中,有以下代码段:
result = 2 + 3
当 Python 编译这段代码时,它会优化常量表达式。这意味着编译器会预先计算 2 + 3
,得到值 5
,然后直接在字节码中使用这个值。因此,你不会看到单独的 2
和 3
在字节码中,而是直接看到它们的和,即 5
。
这种优化是为了提高执行效率。由于 2 + 3
是一个常量表达式(即它的值在编译时就可以确定),没有必要在运行时再次计算它。
在你给出的 dis.dis(add)
输出中,以下这行:
0 LOAD_CONST 1 (5)
正是因为这种优化。这里,LOAD_CONST
直接加载了常量 5
,这个 5
就是 2 + 3
的预计算结果。
Q6:运行这个python程序的时候也没出现pyc文件呀?
A6:将 .py
文件编译为 .pyc
文件的过程是将 Python 源代码编译为字节码。当你在 Python 中导入一个模块时,这一过程会自动发生,并将 .pyc
文件保存在 __pycache__
目录下。
但如果你想手动编译 .py
文件,有以下几种方法:
-
使用
py_compile
模块Python 的标准库中有一个名为
py_compile
的模块,它提供了编译 Python 源文件的功能。import py_compilepy_compile.compile('your_script.py')
运行上述代码后,你会在当前目录的
__pycache__
子目录中找到编译后的.pyc
文件。 -
使用
compileall
模块如果你想一次性编译多个文件或整个目录,你可以使用
compileall
模块。import compileallcompileall.compile_dir('.', force=True)
这将会编译当前目录下的所有
.py
文件。 -
命令行方式
你还可以直接从命令行使用上述模块,不需要写一个脚本。例如:
python -m py_compile your_script.py
或者编译整个目录:
python -m compileall .
无论使用哪种方法,编译后的 .pyc
文件通常会被保存在 __pycache__
目录中,并带有与 Python 解释器版本对应的后缀。例如,对于 Python 3.9,你可能会看到名为 your_script.cpython-39.pyc
的文件。
Q7:python -m py_compile Test.py 是什么含义
A7:在 Python 命令行界面,-m
标志的含义是 “module”,它允许你运行一个 Python 模块作为一个脚本。
当你执行 python -m module_name
时,Python 将会查找名为 module_name
的模块并执行它,就像你直接运行一个脚本文件一样。这种方式特别有用,因为有些模块包含了可以作为脚本执行的代码(即当模块中存在 if __name__ == "__main__":
语句时)。
在你给出的命令 python -m py_compile Test.py
中:
-m
告诉 Python 要运行一个模块。py_compile
是你要运行的模块的名称。Test.py
是传递给py_compile
模块的参数,表示你要编译的 Python 文件。
事实上,这种方式使得你可以方便地运行标准库中的模块,或者其他任何位置的模块,而不需要知道其确切的路径。