==============================
9.25 拆解Pythonå—节ç
==============================
----------
问题
----------
ä½ æƒ³é€šè¿‡å°†ä½ çš„ä»£ç å编译æˆä½Žçº§çš„å—èŠ‚ç æ¥æŸ¥çœ‹å®ƒåº•层的工作机制。
----------
解决方案
----------
``dis`` 模å—å¯ä»¥è¢«ç”¨æ¥è¾“出任何Python函数的å编译结果。例如:
.. code-block:: python
>>> def countdown(n):
... while n > 0:
... print('T-minus', n)
... n -= 1
... print('Blastoff!')
...
>>> import dis
>>> dis.dis(countdown)
2 0 SETUP_LOOP 30 (to 32)
>> 2 LOAD_FAST 0 (n)
4 LOAD_CONST 1 (0)
6 COMPARE_OP 4 (>)
8 POP_JUMP_IF_FALSE 30
3 10 LOAD_GLOBAL 0 (print)
12 LOAD_CONST 2 ('T-minus')
14 LOAD_FAST 0 (n)
16 CALL_FUNCTION 2
18 POP_TOP
4 20 LOAD_FAST 0 (n)
22 LOAD_CONST 3 (1)
24 INPLACE_SUBTRACT
26 STORE_FAST 0 (n)
28 JUMP_ABSOLUTE 2
>> 30 POP_BLOCK
5 >> 32 LOAD_GLOBAL 0 (print)
34 LOAD_CONST 4 ('Blastoff!')
36 CALL_FUNCTION 1
38 POP_TOP
40 LOAD_CONST 0 (None)
42 RETURN_VALUE
>>>
----------
讨论
----------
å½“ä½ æƒ³è¦çŸ¥é“ä½ çš„ç¨‹åºåº•层的è¿è¡Œæœºåˆ¶çš„æ—¶å€™ï¼Œ``dis`` æ¨¡å—æ˜¯å¾ˆæœ‰ç”¨çš„ã€‚æ¯”å¦‚å¦‚æžœä½ æƒ³è¯•ç€ç†è§£æ€§èƒ½ç‰¹å¾ã€‚
被 ``dis()`` 函数解æžçš„原始å—节ç 如下所示:
.. code-block:: python
>>> countdown.__code__.co_code
b"x'\x00|\x00\x00d\x01\x00k\x04\x00r)\x00t\x00\x00d\x02\x00|\x00\x00\x83
\x02\x00\x01|\x00\x00d\x03\x008}\x00\x00q\x03\x00Wt\x00\x00d\x04\x00\x83
\x01\x00\x01d\x00\x00S"
>>>
å¦‚æžœä½ æƒ³è‡ªå·±è§£é‡Šè¿™æ®µä»£ç ï¼Œä½ éœ€è¦ä½¿ç”¨ä¸€äº›åœ¨ ``opcode`` 模å—ä¸å®šä¹‰çš„常é‡ã€‚例如:
.. code-block:: python
>>> c = countdown.__code__.co_code
>>> import opcode
>>> opcode.opname[c[0]]
'SETUP_LOOP'
>>> opcode.opname[c[2]]
'LOAD_FAST'
>>>
奇怪的是,在 ``dis`` 模å—ä¸å¹¶æ²¡æœ‰å‡½æ•°è®©ä½ 以编程方å¼å¾ˆå®¹æ˜“çš„æ¥å¤„ç†å—节ç 。
ä¸è¿‡ï¼Œä¸‹é¢çš„生æˆå™¨å‡½æ•°å¯ä»¥å°†åŽŸå§‹å—节ç åºåˆ—è½¬æ¢æˆ ``opcodes`` å’Œå‚æ•°ã€‚
.. code-block:: python
import opcode
def generate_opcodes(codebytes):
extended_arg = 0
i = 0
n = len(codebytes)
while i < n:
op = codebytes[i]
i += 1
if op >= opcode.HAVE_ARGUMENT:
oparg = codebytes[i] + codebytes[i+1]*256 + extended_arg
extended_arg = 0
i += 2
if op == opcode.EXTENDED_ARG:
extended_arg = oparg * 65536
continue
else:
oparg = None
yield (op, oparg)
使用方法如下:
.. code-block:: python
>>> for op, oparg in generate_opcodes(countdown.__code__.co_code):
... print(op, opcode.opname[op], oparg)
è¿™ç§æ–¹å¼å¾ˆå°‘有人知é“ï¼Œä½ å¯ä»¥åˆ©ç”¨å®ƒæ›¿æ¢ä»»ä½•ä½ æƒ³è¦æ›¿æ¢çš„函数的原始å—节ç 。
䏋颿ˆ‘ä»¬ç”¨ä¸€ä¸ªç¤ºä¾‹æ¥æ¼”示整个过程:
.. code-block:: python
>>> def add(x, y):
... return x + y
...
>>> c = add.__code__
>>> c
<code object add at 0x1007beed0, file "<stdin>", line 1>
>>> c.co_code
b'|\x00\x00|\x01\x00\x17S'
>>>
>>> # Make a completely new code object with bogus byte code
>>> import types
>>> newbytecode = b'xxxxxxx'
>>> nc = types.CodeType(c.co_argcount, c.co_kwonlyargcount,
... c.co_nlocals, c.co_stacksize, c.co_flags, newbytecode, c.co_consts,
... c.co_names, c.co_varnames, c.co_filename, c.co_name,
... c.co_firstlineno, c.co_lnotab)
>>> nc
<code object add at 0x10069fe40, file "<stdin>", line 1>
>>> add.__code__ = nc
>>> add(2,3)
Segmentation fault
ä½ å¯ä»¥åƒè¿™æ ·è€å¤§æ‹›è®©è§£é‡Šå™¨å¥”溃。但是,对于编写更高级优化和元编程工具的程åºå‘˜æ¥è®²ï¼Œ
他们å¯èƒ½çœŸçš„需è¦é‡å†™å—节ç 。本节最åŽçš„éƒ¨åˆ†æ¼”ç¤ºäº†è¿™ä¸ªæ˜¯æ€Žæ ·åšåˆ°çš„ã€‚ä½ è¿˜å¯ä»¥å‚考å¦å¤–一个类似的例å:
`this code on ActiveState <http://code.activestate.com/recipes/277940-decorator-for-bindingconstants-at-compile-time/>`_