下文中,若Python指Python解释器,如无特别注明,指的是CPython 3.6
Python中,一个函数f的code object可以从f.__code__得到
>>> def f(): pass
...
>>> f.__code__
<code object f at 0x7f0000000000, file "<stdin>", line 1>用dis.show_code可以显示code object的详细信息
>>> show_code(f)
Name: f
Filename: <stdin>
Argument count: 0
Kw-only arguments: 0
Number of locals: 0
Stack size: 1
Flags: OPTIMIZED, NEWLOCALS, NOFREE
Constants:
0: None一个函数的code object,Name就是函数名。如果是lambda,Name是<lambda>
>>> show_code(lambda: None)
Name: <lambda>
Filename: <stdin>
Argument count: 0
Kw-only arguments: 0
Number of locals: 0
Stack size: 1
Flags: OPTIMIZED, NEWLOCALS, NOFREE
Constants:
0: NoneFilename就是函数定义所在的文件名,如果是在Shell中输入的,就会是<stdin>
而Python代码会编译成字节码,可以通过dis.dis查看
>>> dis(f)
1 0 LOAD_CONST 0 (None)
2 RETURN_VALUEConstants是常量表,LOAD_CONST 0就是取出常量表中第0个元素。
code object是不能修改的
>>> f.__code__.co_name = "x"
Traceback (most recent call last):
File "<stdin>", line 1, in <module>
AttributeError: readonly attribute可以建一个新的替代,比如换一个不同的常量表
>>> from types import CodeType
>>> def replace(obj, **kwargs):
... args = ("argcount", "kwonlyargcount", "nlocals", "stacksize", "flags", "code", "consts", "names", "varnames", "filename" ,"name", "firstlineno", "lnotab", "freevars", "cellvars")
... return CodeType(*(kwargs.get(a, getattr(obj, "co_"+a)) for a in args))
...
>>> f.__code__ = replace(f.__code__, consts=(1,))
>>> show_code(f)
Name: f
Filename: <stdin>
Argument count: 0
Kw-only arguments: 0
Number of locals: 0
Stack size: 1
Flags: OPTIMIZED, NEWLOCALS, NOFREE
Constants:
0: 1
>>> f()
1Code object不只用于普通函数,所有Python代码都会有Code object,不同的用途可能会需要设置不同的Flag
嵌套函数要有Flag NESTED
>>> def f():
... def g(): pass
... show_code(g)
...
>>> f()
Name: g
Filename: <stdin>
Argument count: 0
Kw-only arguments: 0
Number of locals: 0
Stack size: 1
Flags: OPTIMIZED, NEWLOCALS, NESTED, NOFREE
Constants:
0: NoneGenerator要有Flag GENERATOR
>>> def g(): yield
...
>>> show_code(f)
Name: f
Filename: <stdin>
Argument count: 0
Kw-only arguments: 0
Number of locals: 0
Stack size: 1
Flags: OPTIMIZED, NEWLOCALS, GENERATOR, NOFREE
Constants:
0: NoneCoroutine要有Flag COROUTINE
>>> async def c(): pass
...
>>> show_code(c)
Name: c
Filename: <stdin>
Argument count: 0
Kw-only arguments: 0
Number of locals: 0
Stack size: 1
Flags: OPTIMIZED, NEWLOCALS, NOFREE, COROUTINE
Constants:
0: NonePEP-492 Iterable Coroutine要有Flag ITERABLE_COROUTINE
>>> from types import coroutine
>>> @coroutine
... def ic(): yield
...
>>> show_code(ic)
Name: ic
Filename: <stdin>
Argument count: 0
Kw-only arguments: 0
Number of locals: 0
Stack size: 1
Flags: OPTIMIZED, NEWLOCALS, GENERATOR, NOFREE, ITERABLE_COROUTINE
Constants:
0: NonePEP-535 Async Generator要有Flag ASYNC_GENERATOR
>>> async def ag(): yield
...
>>> show_code(ag)
Name: ag
Filename: <stdin>
Argument count: 0
Kw-only arguments: 0
Number of locals: 0
Stack size: 1
Flags: OPTIMIZED, NEWLOCALS, NOFREE, ASYNC_GENERATOR
Constants:
0: None注意Class是没有Flag OPTIMIZED和NEWLOCALS的
>>> code = compile("class A: x = 1", "<stdin>", "exec")
>>> show_code(code.co_consts[0])
Name: A
Filename: <stdin>
Argument count: 0
Kw-only arguments: 0
Number of locals: 0
Stack size: 1
Flags: NOFREE
Constants:
0: 'A'
1: 1
2: None
Names:
0: __name__
1: __module__
2: __qualname__
3: x
>>> d = {}
>>> exec(code.co_consts[0], globals(), d)
>>> A = type('A', (), d)
>>> A.x
1Module也是没有Flag OPTIMIZED和NEWLOCALS的
>>> code = compile("x = 1", "<stdin>", "exec")
>>> show_code(code)
Name: <module>
Filename: <stdin>
Argument count: 0
Kw-only arguments: 0
Number of locals: 0
Stack size: 1
Flags: NOFREE
Constants:
0: 1
1: None
Names:
0: x
>>> from types import ModuleType
>>> mod = ModuleType("example")
>>> exec(code, mod.__dict__, mod.__dict__)
>>> import sys
>>> sys.modules["example"] = mod
>>> import example
>>> example.x
1设置了NEWLOCALS,每次都会创建一个新的locals
>>> code = compile("x = 1", "<stdin>", "exec")
>>> d = {}
>>> exec(code, globals(), d)
>>> d
{'x': 1}
>>> code = replace(code, flags = code.co_flags|2)
>>> show_code(code)
Name: <module>
Filename: <stdin>
Argument count: 0
Kw-only arguments: 0
Number of locals: 0
Stack size: 1
Flags: NEWLOCALS, NOFREE
Constants:
0: 1
1: None
Names:
0: x
>>> d = {}
>>> exec(code, globals(), d)
>>> d
{}可以看到不会影响传入的locals
主要有三个和全局变量相关的Opcode,LOAD_GLOBAL,STORE_GLOBAL和DELETE_GLOBAL,举个例子
>>> def f():
... def g():
... global x
... x = 1
... show_code(g)
... dis(g)
...
>>> f()
Name: g
Filename: <stdin>
Argument count: 0
Kw-only arguments: 0
Number of locals: 0
Stack size: 1
Flags: OPTIMIZED, NEWLOCALS, NESTED, NOFREE
Constants:
0: None
1: 1
Names:
0: x
4 0 LOAD_CONST 1 (1)
2 STORE_GLOBAL 0 (x)
4 LOAD_CONST 0 (None)
6 RETURN_VALUE主要有三个和局部变量相关的Opcode,LOAD_FAST,STORE_FAST和DELETE_FAST,举个例子
>>> def f():
... def g():
... x = 1
... show_code(g)
... dis(g)
...
>>> f()
Name: g
Filename: <stdin>
Argument count: 0
Kw-only arguments: 0
Number of locals: 1
Stack size: 1
Flags: OPTIMIZED, NEWLOCALS, NESTED, NOFREE
Constants:
0: None
1: 1
Variable names:
0: x
3 0 LOAD_CONST 1 (1)
2 STORE_FAST 0 (x)
4 LOAD_CONST 0 (None)
6 RETURN_VALUE所有的参数都是局部变量,可以看到Number of locals为1。而Argument count是参数个数
>>> def f1(a): pass
...
>>> show_code(f1)
Name: f1
Filename: <stdin>
Argument count: 1
Kw-only arguments: 0
Number of locals: 1
Stack size: 1
Flags: OPTIMIZED, NEWLOCALS, NOFREE
Constants:
0: None
Variable names:
0: aKw-only arguments是只接受keyword形式的参数个数
>>> def f2(a, *, b): pass
...
>>> show_code(f2)
Name: f2
Filename: <stdin>
Argument count: 1
Kw-only arguments: 1
Number of locals: 2
Stack size: 1
Flags: OPTIMIZED, NEWLOCALS, NOFREE
Constants:
0: None
Variable names:
0: a
1: bFlag VARARGS
>>> def f3(*args): pass
...
>>> show_code(f3)
Name: f3
Filename: <stdin>
Argument count: 0
Kw-only arguments: 0
Number of locals: 1
Stack size: 1
Flags: OPTIMIZED, NEWLOCALS, VARARGS, NOFREE
Constants:
0: None
Variable names:
0: argsFlag VARKEYWORDS
>>> def f4(**kwargs): pass
...
>>> show_code(f4)
Name: f4
Filename: <stdin>
Argument count: 0
Kw-only arguments: 0
Number of locals: 1
Stack size: 1
Flags: OPTIMIZED, NEWLOCALS, VARKEYWORDS, NOFREE
Constants:
0: None
Variable names:
0: kwargsVariable names的顺序
>>> def f5(a, *b, c, **d): pass
...
>>> show_code(f5)
Name: f5
Filename: <stdin>
Argument count: 1
Kw-only arguments: 1
Number of locals: 4
Stack size: 1
Flags: OPTIMIZED, NEWLOCALS, VARARGS, VARKEYWORDS, NOFREE
Constants:
0: None
Variable names:
0: a
1: c
2: b
3: d主要有三个和Free/Cell变量相关的Opcode,LOAD_DEREF,STORE_DEREF和DELETE_DEREF,举个例子
>>> def f():
... x = 1
... def g():
... return x
... show_code(g)
... dis(g)
...
>>> f()
Name: g
Filename: <stdin>
Argument count: 0
Kw-only arguments: 0
Number of locals: 0
Stack size: 1
Flags: OPTIMIZED, NEWLOCALS, NESTED
Constants:
0: None
Free variables:
0: x
4 0 LOAD_DEREF 0 (x)
2 RETURN_VALUE而上层作用域中的局部变量变成了Cell变量,而在MAKE_FUNCTION之前需要先用LOAD_CLOSURE,BUILD_TUPLE生成一个tuple
>>> show_code(f)
Name: f
Filename: <stdin>
Argument count: 0
Kw-only arguments: 0
Number of locals: 1
Stack size: 3
Flags: OPTIMIZED, NEWLOCALS
Constants:
0: None
1: 1
2: <code object g at 0x7f0000001000, file "<stdin>", line 3>
3: 'f.<locals>.g'
Names:
0: show_code
1: dis
Variable names:
0: g
Cell variables:
0: x
2 0 LOAD_CONST 1 (1)
2 STORE_DEREF 0 (x)
3 4 LOAD_CLOSURE 0 (x)
6 BUILD_TUPLE 1
8 LOAD_CONST 2 (<code object g at 0x7f0000001000, file "<stdin>", line 3>)
10 LOAD_CONST 3 ('f.<locals>.g')
12 MAKE_FUNCTION 8
14 STORE_FAST 0 (g)
5 16 LOAD_GLOBAL 0 (show_code)
18 LOAD_FAST 0 (g)
20 CALL_FUNCTION 1
22 POP_TOP
6 24 LOAD_GLOBAL 1 (dis)
26 LOAD_FAST 0 (g)
28 CALL_FUNCTION 1
30 POP_TOP
32 LOAD_CONST 0 (None)
34 RETURN_VALUE另外,有了Free或Cell变量,就不能有Flag NOFREE了
有Flag OPTIMIZED的code object在编译的时候就已经确定了每个变量的作用域。而对于Module来说,globals和locals是相同的,并且locals的个数并不能在编译期确定,于是不能设置Flag OPTIMIZED,操作变量用LOAD_NAME,STORE_NAME和DELETE_NAME。这三个Opcode是在运行期确定变量的作用域。比如
>>> code = compile("x = 1", "<stdin>", "exec")
>>> show_code(code)
Name: <module>
Filename: <stdin>
Argument count: 0
Kw-only arguments: 0
Number of locals: 0
Stack size: 1
Flags: NOFREE
Constants:
0: 1
1: None
Names:
0: x
>>> dis(code)
1 0 LOAD_CONST 0 (1)
2 STORE_NAME 0 (x)
4 LOAD_CONST 1 (None)
6 RETURN_VALUE注意这里Number of locals是0
参考CPython Objects/lnotab_notes.txt
Code object的Stack size必须大于等于执行过程中可能的最大Stack size,不然可能会引发解释器崩溃