Python源码中的PyCodeObject

1.Python程序的执行过程

Python解释器(interpreter)在执行任何一个Python程序文件时,首先进行的动作都是先对文件中的Python源代码进行编译,编译的主要结果是产生的一组Python的字节码(byte code),然后将编译的结果交给Python虚拟机(Virtual Machine),由虚拟机按照顺序一条一条地执行字节码,从而完成对Python程序的执行动作。

对比java的执行:

java:     .java-->(javac)-->.class-->(java)-->结果

python: .py  -->(编译器)-->.pyc-->(虚拟机)-->结果

从上面可知:Python对源码的编译结果是产生的一个.pyc文件,这其实并不太准确。

如:

~/code/py/dis/demo.py

1 class A:
2     pass
3 def f():
4     pass
5 a = A()
6 f()

执行:python demo.py,并没有产生.pyc文件。

分析如下:

对于Python编译器来说,PyCodeObject对象是其真正的编译结果,而pyc文件只是这个对象在硬盘上的表现形式。

在程序的运行期间,编译结果存在于内存的PyCodeObject对象中;而运行结束后,这个编译结果又被保存到pyc文件中。当下次运行相同的程序时,Python会根据pyc文件中记录的编译结果直接建立内存中PyCodeObject对象,而不用再次对源码进行编译了。

把py文件的编译结果保存到pyc文件,最大的优点在于在运行程序时,不需要对该源码重新进行编译。所以,需要编译形成pyc文的应该是那些可以重用的代码。

对于仅仅运行一次的程序,保存其对于的pyc文件时没有必要的。

如果程序要执行import导入模块的操作,程序运行时会触发pyc文件的生成(如果该模块在PATH路径下与之已有匹配的pyc文件,不需生成直接使用即可)。

2.Python源码中的PyCodeObject

Include/code.h:

 1 /* Bytecode object */
 2 typedef struct {
 3     PyObject_HEAD
 4     int co_argcount;             /* #arguments, except *args */
 5     int co_kwonlyargcount;       /* #keyword only arguments */
 6     int co_nlocals;              /* #local variables */
 7     int co_stacksize;            /* #entries needed for evaluation stack */
 8     int co_flags;                /* CO_..., see below */
 9     PyObject *co_code;           /* instruction opcodes */
10     PyObject *co_consts;         /* list (constants used) */
11     PyObject *co_names;          /* list of strings (names used) */
12     PyObject *co_varnames;       /* tuple of strings (local variable names) */
13     PyObject *co_freevars;       /* tuple of strings (free variable names) */
14     PyObject *co_cellvars;       /* tuple of strings (cell variable names) */
15     /* The rest doesn‘t count for hash or comparisons */
16     unsigned char *co_cell2arg;  /* Maps cell vars which are arguments. */
17     PyObject *co_filename;       /* unicode (where it was loaded from) */
18     PyObject *co_name;           /* unicode (name, for reference) */
19     int co_firstlineno;          /* first source line number */
20     PyObject *co_lnotab;         /* string (encoding addr<->lineno mapping) See
21                                     Objects/lnotab_notes.txt for details. */
22     void *co_zombieframe;        /* for optimization only (see frameobject.c) */
23     PyObject *co_weakreflist;    /* to support weakrefs to code objects */
24 } PyCodeObject;

在解释PyCodeObject中各个域的含义之前,先说明一个概念--Code Block:

Python编译器在对Python源码进行编译的时候,对代码中的一个Code Block,会创建一个PyCodeObject对象与这段代码对应。

如何确定多少代码算一个Code Block?

Python中确定Code Block的规则:当进入一个新的名字空间或作用域时,就算进入了一个新的Code Block了。

即:一个名字空间对应一个Code Block,它会对应一个PyCodeObject。

现在暂且认为名字空间就是符号的上下文环境,是名字到对象的映射。名字空间以后会详述。

在Python中,类、函数和module都对应着一个独立的名字空间,因此都会对应一个PyCodeObject对象。

例如:

~/code/py/dis/demo.py

1 class A:
2     pass
3 def f():
4     pass
5 a = A()
6 f()

Python编译器对demo.py源码编译之后,会创建3个PyCodeObject对象:第一个是对应整个demo.py文件代表的Code Block,第二个是对应Class A代表的Code Block,第三个是对应f代表的Code Block。

PyCodeObject中各个域的含义:

(1)co_argcount、co_kwonlyargcount

PEP 3102:http://www.python.org/dev/peps/pep-3102/

Keyword-only argument:在函数参数列表中,出现在*varargs之后的命名参数只能使用关键参数的形式调用。

函数调用是参数的赋值顺序:位置参数-->关键字参数-->可变参数(*varargs)

co_argcount:CodeBlock中位置参数的个数,即:在调用时出现的位置参数的个数(不包含可变参数*varargs)。

co_kwonlyargcount:CodeBlock中的关键参数的个数,即在调用时是出现在可变参数(*varargs)之后的参数个数,可变参数之后的参数均是形式为“keyvalue”的关键参数。

>>> def f1(a, b, c, *d, e, f):
...     m = 1
...     pass
...
>>> f1.__code__.co_argcount
3
>>> f1.__code__.co_kwonlyargcount
2

(2)co_nlocals:Code Block中的所有局部变量的个数,包括code block的参数(co_argcount+co_kwonlyargcount+可变参数个数)+code block内的局部变量

>>> f1.__code__.co_nlocals
7

7 = (3+2+1)+1

(3)co_stacksize:执行该段Code Block需要的栈空间数

>>> f1.__code__.co_stacksize
1

(4)co_code:Code Block编译所得的字节码指令序列

>>> f1.__code__.co_code
b‘d\x01\x00}\x06\x00d\x00\x00S‘

(5)co_consts、co_names

co_consts:Code Block中的所有常量的元组

co_names:Code Block中的所有符号(名字)的元组

>>> f1.__code__.co_consts
(None, 1)
>>> f1.__code__.co_names
()

(6)co_filename、co_name

co_filename:Code Block所对应的的.py文件的完整路径

co_name:Code Block的名字,,通常是函数名或类名

>>> f1.__code__.co_filename
‘<stdin>‘
>>> f1.__code__.co_name
‘f1‘

非交互式运行结果:

vim demo1.py

def f1(a, b, c, *d, e, f):
    m = 1
    pass
print(f1.__code__.co_filename)
print(f1.__code__.co_name)

运行:

[[email protected] dis]# python demo1.py
demo1.py
f1

(7)co_firstlineno:Code Block在对应的.py文件中的起始行

demo1.py

def f1(a, b, c, *d, e, f):
    m = 1
    pass
print(f1.__code__.co_firstlineno)

运行:

[[email protected] dis]# python demo1.py
1

(8)co_varnames、co_freevars、co_cellvars

co_varnames:在本代码段中被赋值,但没有被内层代码段引用的变量

co_freevars(freevars:自由变量):在本代码段中被引用,在外层代码段中被赋值的变量

co_cellvars(cellvars:被内层代码所约束的变量):在本代码段中被赋值,且被内层代码段引用的变量

例1:简单函数

vim demo1.py:

 1 def f1(a, b, c, *d, e, f):
 2     m = 1
 3     pass
 4
 5 print(‘co_argcount        :‘, f1.__code__.co_argcount)
 6 print(‘co_kwonlyargcount  :‘, f1.__code__.co_kwonlyargcount)
 7 print(‘co_nlocals         :‘, f1.__code__.co_nlocals)
 8 print(‘co_stacksize       :‘, f1.__code__.co_stacksize)
 9 print(‘co_flags           :‘, f1.__code__.co_flags)
10 print(‘co_code            :‘, f1.__code__.co_code)
11 print(‘co_consts          :‘, f1.__code__.co_consts)
12 print(‘co_names           :‘, f1.__code__.co_names)
13 print(‘co_varnames        :‘, f1.__code__.co_varnames)
14 print(‘co_freevars        :‘, f1.__code__.co_freevars)
15 print(‘co_cellvars        :‘, f1.__code__.co_cellvars)
16 print(‘co_filename        :‘, f1.__code__.co_filename)
17 print(‘co_name            :‘, f1.__code__.co_name)
18 print(‘co_firstlineno     :‘, f1.__code__.co_firstlineno)
19 print(‘co_lnotab          :‘, f1.__code__.co_lnotab)

运行:

[[email protected] dis]# python demo1.py
co_argcount        : 3
co_kwonlyargcount  : 2
co_nlocals         : 7
co_stacksize       : 1
co_flags           : 71
co_code            : b‘d\x01\x00}\x06\x00d\x00\x00S‘
co_consts          : (None, 1)
co_names           : ()
co_varnames        : (‘a‘, ‘b‘, ‘c‘, ‘e‘, ‘f‘, ‘d‘, ‘m‘)
co_freevars        : ()
co_cellvars        : ()
co_filename        : demo1.py
co_name            : f1
co_firstlineno     : 1
co_lnotab          : b‘\x00\x01\x06\x01‘

例2:嵌套函数

vim demo2.py

 1 def f1(a, b, c, *d, e, f):
 2     m = 1
 3     def f2():
 4         n = m
 5     print(‘f2-->co_argcount        :‘, f2.__code__.co_argcount)
 6     print(‘f2-->co_kwonlyargcount  :‘, f2.__code__.co_kwonlyargcount)
 7     print(‘f2-->co_nlocals         :‘, f2.__code__.co_nlocals)
 8     print(‘f2-->co_stacksize       :‘, f2.__code__.co_stacksize)
 9     print(‘f2-->co_flags           :‘, f2.__code__.co_flags)
10     print(‘f2-->co_code            :‘, f2.__code__.co_code)
11     print(‘f2-->co_consts          :‘, f2.__code__.co_consts)
12     print(‘f2-->co_names           :‘, f2.__code__.co_names)
.co_varnames)
16     print(‘f2-->co_filename        :‘, f2.__code__.co_filename)
17     print(‘f2-->co_name            :‘, f2.__code__.co_name)
18     print(‘f2-->co_firstlineno     :‘, f2.__code__.co_firstlineno)
19     print(‘f2-->co_lnotab          :‘, f2.__code__.co_lnotab)
20
21 print(‘f1-->co_argcount        :‘, f1.__code__.co_argcount)
22 print(‘f1-->co_kwonlyargcount  :‘, f1.__code__.co_kwonlyargcount)
23 print(‘f1-->co_nlocals         :‘, f1.__code__.co_nlocals)
24 print(‘f1-->co_stacksize       :‘, f1.__code__.co_stacksize)
25 print(‘f1-->co_flags           :‘, f1.__code__.co_flags)
26 print(‘f1-->co_code            :‘, f1.__code__.co_code)
27 print(‘f1-->co_consts          :‘, f1.__code__.co_consts)
28 print(‘f1-->co_names           :‘, f1.__code__.co_names)
.co_varnames)
32 print(‘f1-->co_filename        :‘, f1.__code__.co_filename)
33 print(‘f1-->co_name            :‘, f1.__code__.co_name)
34 print(‘f1-->co_firstlineno     :‘, f1.__code__.co_firstlineno)
35 print(‘f1-->co_lnotab          :‘, f1.__code__.co_lnotab)
36 print(‘=========================================================‘)
37 f1(1, 2, 3, 4, 5, 6, 7, e = 8, f = 9)

运行:

 1 [[email protected] dis]# python demo2.py
 2 f1-->co_argcount        : 3
 3 f1-->co_kwonlyargcount  : 2
 4 f1-->co_nlocals         : 7
 5 f1-->co_stacksize       : 3
 6 f1-->co_flags           : 7
 7 f1-->co_code            : b‘d\x01\x00\x89\x00\x00\x87\x00\x00f\x01\x00d\x02\x00d\x03\x00\x86\x00\x00}\x06\x00t\x00\x00d\x04\x00|\x06\x00j\x01\x00j\x02\x00\x83\x02\x00\x01t\x00\x00d\x05\x00|\x06\x00j\x01\x00j\x03\x00\x83\x02\x00\x01t\x00\x00d\x06\x00|\x06\x00j\x01\x00j\x04\x00\x83\x02\x00\x01t\x00\x00d\x07\x00|\x06\x00j\x01\x00j\x05\x00\x83\x02\x00\x01t\x00\x00d\x08\x00|\x06\x00j\x01\x00j\x06\x00\x83\x02\x00\x01t\x00\x00d\t\x00|\x06\x00j\x01\x00j\x07\x00\x83\x02\x00\x01t\x00\x00d\n\x00|\x06\x00j\x01\x00j\x08\x00\x83\x02\x00\x01t\x00\x00d\x0b\x00|\x06\x00j\x01\x00j\t\x00\x83\x02\x00\x01t\x00\x00d\x0c\x00|\x06\x00j\x01\x00j\n\x00\x83\x02\x00\x01t\x00\x00d\r\x00|\x06\x00j\x01\x00j\x0b\x00\x83\x02\x00\x01t\x00\x00d\x0e\x00|\x06\x00j\x01\x00j\x0c\x00\x83\x02\x00\x01t\x00\x00d\x0f\x00|\x06\x00j\x01\x00j\r\x00\x83\x02\x00\x01t\x00\x00d\x10\x00|\x06\x00j\x01\x00j\x0e\x00\x83\x02\x00\x01t\x00\x00d\x11\x00|\x06\x00j\x01\x00j\x0f\x00\x83\x02\x00\x01t\x00\x00d\x12\x00|\x06\x00j\x01\x00j\x10\x00\x83\x02\x00\x01d\x00\x00S‘
 8 f1-->co_consts          : (None, 1, <code object f2 at 0x7f5c2f036930, file "demo2.py", line 3>, ‘f1.<locals>.f2‘, ‘f2-->co_argcount        :‘, ‘f2-->co_kwonlyargcount  :‘, ‘f2-->co_nlocals         :‘, ‘f2-->co_stacksize       :‘, ‘f2-->co_flags           :‘, ‘f2-->co_code            :‘, ‘f2-->co_consts          :‘, ‘f2-->co_names           :‘, ‘f2-->co_varnames        :‘, ‘f2-->co_freevars        :‘, ‘f2-->co_cellvars        :‘, ‘f2-->co_filename        :‘, ‘f2-->co_name            :‘, ‘f2-->co_firstlineno     :‘, ‘f2-->co_lnotab          :‘)
 9 f1-->co_names           : (‘print‘, ‘__code__‘, ‘co_argcount‘, ‘co_kwonlyargcount‘, ‘co_nlocals‘, ‘co_stacksize‘, ‘co_flags‘, ‘co_code‘, ‘co_consts‘, ‘co_names‘, ‘co_varnames‘, ‘co_freevars‘, ‘co_cellvars‘, ‘co_filename‘, ‘co_name‘, ‘co_firstlineno‘, ‘co_lnotab‘)
)
13 f1-->co_filename        : demo2.py
14 f1-->co_name            : f1
15 f1-->co_firstlineno     : 1
16 f1-->co_lnotab          : b‘\x00\x01\x06\x01\x12\x02\x13\x01\x13\x01\x13\x01\x13\x01\x13\x01\x13\x01\x13\x01\x13\x01\x13\x01\x13\x01\x13\x01\x13\x01\x13\x01\x13\x01‘
17 =========================================================
18 f2-->co_argcount        : 0
19 f2-->co_kwonlyargcount  : 0
20 f2-->co_nlocals         : 1
21 f2-->co_stacksize       : 1
22 f2-->co_flags           : 19
23 f2-->co_code            : b‘\x88\x00\x00}\x00\x00d\x00\x00S‘
24 f2-->co_consts          : (None,)
25 f2-->co_names           : ()
,)
29 f2-->co_filename        : demo2.py
30 f2-->co_name            : f2
31 f2-->co_firstlineno     : 3
32 f2-->co_lnotab          : b‘\x00\x01‘

例3:闭包

vim demo3.py

 1 def f1(a, b, c, *d, e, f):
 2     m = 1
 3     def f2():
 4         n = m
 5     return f2
 6
 7 print(‘f1-->co_argcount        :‘, f1.__code__.co_argcount)
 8 print(‘f1-->co_kwonlyargcount  :‘, f1.__code__.co_kwonlyargcount)
 9 print(‘f1-->co_nlocals         :‘, f1.__code__.co_nlocals)
10 print(‘f1-->co_stacksize       :‘, f1.__code__.co_stacksize)
11 print(‘f1-->co_flags           :‘, f1.__code__.co_flags)
12 print(‘f1-->co_code            :‘, f1.__code__.co_code)
13 print(‘f1-->co_consts          :‘, f1.__code__.co_consts)
14 print(‘f1-->co_names           :‘, f1.__code__.co_names)
.co_varnames)
18 print(‘f1-->co_filename        :‘, f1.__code__.co_filename)
19 print(‘f1-->co_name            :‘, f1.__code__.co_name)
20 print(‘f1-->co_firstlineno     :‘, f1.__code__.co_firstlineno)
21 print(‘f1-->co_lnotab          :‘, f1.__code__.co_lnotab)
22 print(‘=========================================================‘)
23 f3 = f1(1, 2, 3, 4, 5, 6, 7, e = 8, f = 9)
24 print(‘f3-->co_argcount        :‘, f3.__code__.co_argcount)
25 print(‘f3-->co_kwonlyargcount  :‘, f3.__code__.co_kwonlyargcount)
26 print(‘f3-->co_nlocals         :‘, f3.__code__.co_nlocals)
27 print(‘f3-->co_stacksize       :‘, f3.__code__.co_stacksize)
28 print(‘f3-->co_flags           :‘, f3.__code__.co_flags)
29 print(‘f3-->co_code            :‘, f3.__code__.co_code)
30 print(‘f3-->co_consts          :‘, f3.__code__.co_consts)
31 print(‘f3-->co_names           :‘, f3.__code__.co_names)
32 print(‘f3-->co_varnames        :‘, f3.__code__.co_varnames)
33 print(‘f3-->co_freevars        :‘, f3.__code__.co_freevars)
34 print(‘f3-->co_cellvars        :‘, f3.__code__.co_cellvars)
35 print(‘f3-->co_filename        :‘, f3.__code__.co_filename)
36 print(‘f3-->co_name            :‘, f3.__code__.co_name)
37 print(‘f3-->co_firstlineno     :‘, f3.__code__.co_firstlineno)
38 print(‘f3-->co_lnotab          :‘, f3.__code__.co_lnotab)

运行:

 1 [[email protected] dis]# python demo3.py
 2 f1-->co_argcount        : 3
 3 f1-->co_kwonlyargcount  : 2
 4 f1-->co_nlocals         : 7
 5 f1-->co_stacksize       : 3
 6 f1-->co_flags           : 7
 7 f1-->co_code            : b‘d\x01\x00\x89\x00\x00\x87\x00\x00f\x01\x00d\x02\x00d\x03\x00\x86\x00\x00}\x06\x00|\x06\x00S‘
 8 f1-->co_consts          : (None, 1, <code object f2 at 0x7f010517e930, file "demo3.py", line 3>, ‘f1.<locals>.f2‘)
 9 f1-->co_names           : ()
)
13 f1-->co_filename        : demo3.py
14 f1-->co_name            : f1
15 f1-->co_firstlineno     : 1
16 f1-->co_lnotab          : b‘\x00\x01\x06\x01\x12\x02‘
17 =========================================================
18 f3-->co_argcount        : 0
19 f3-->co_kwonlyargcount  : 0
20 f3-->co_nlocals         : 1
21 f3-->co_stacksize       : 1
22 f3-->co_flags           : 19
23 f3-->co_code            : b‘\x88\x00\x00}\x00\x00d\x00\x00S‘
24 f3-->co_consts          : (None,)
25 f3-->co_names           : ()
26 f3-->co_varnames        : (‘n‘,)
27 f3-->co_freevars        : (‘m‘,)
28 f3-->co_cellvars        : ()
29 f3-->co_filename        : demo3.py
30 f3-->co_name            : f2
31 f3-->co_firstlineno     : 3
32 f3-->co_lnotab          : b‘\x00\x01‘

(9)co_lnotab:字节码指令与.pyc文件中的source code行号的对于关系

Object/lnotab_notes.txt:

All about co_lnotab, the line number table.

Code objects store a field named co_lnotab. This is an array of unsigned bytes disguised as a Python string. It is used to map bytecode offsets to source code line #s for tracebacks and to identify line number boundaries for line tracing.

The array is conceptually a compressed list of (bytecode offset increment, line number increment) pairs. The details are important and delicate, best illustrated by example:

byte code offset               source code line number
     0                                              1
     6                                              2
     50                                            7
     350                                          307
     361                                          308

Instead of storing these numbers literally, we compress the list by storing only the increments from one row to the next.Conceptually, the stored list might look like:

0, 1, 6, 1, 44, 5, 300, 300, 11, 1

形成的数组:0, 1, (0+6), (1+1), (6+44), (2+5), (50+300), (7+300), (350+11), (307+1)

3.在Python中访问PyCodeObject对象

以上的例子中已经有一个函数的属性可以访问函数对应的PyCodeObject对象:__code__,函数的该属性表示已经编译函数体的Code Object。

在Python中,可以通过code对象访问PyCodeObject对象中的各个域。code对象是对C一级的PyCodeObject对象的一个简单包装。

通过内建函数compile可以获得一个code对象。

例如:

demo.py:

class A:
    pass
def f():
    pass
a = A()
f()

交互式:

>>> source = open(‘./demo.py‘).read()
>>> source
‘class A:\n    pass\ndef f():\n    pass\na = A()\nf()\n‘
>>> co = compile(source, ‘./demo.py‘, ‘exec‘)
>>> type(co)
<class ‘code‘>
>>> dir(co)
[‘__class__‘, ‘__delattr__‘, ‘__dir__‘, ‘__doc__‘, ‘__eq__‘, ‘__format__‘, ‘__ge__‘, ‘__getattribute__‘, ‘__gt__‘, ‘__hash__‘, ‘__init__‘, ‘__le__‘, ‘__lt__‘, ‘__ne__‘, ‘__new__‘, ‘__reduce__‘, ‘__reduce_ex__‘, ‘__repr__‘, ‘__setattr__‘, ‘__sizeof__‘, ‘__str__‘, ‘__subclasshook__‘, ‘co_argcount‘, ‘co_cellvars‘, ‘co_code‘, ‘co_consts‘, ‘co_filename‘, ‘co_firstlineno‘, ‘co_flags‘, ‘co_freevars‘, ‘co_kwonlyargcount‘, ‘co_lnotab‘, ‘co_name‘, ‘co_names‘, ‘co_nlocals‘, ‘co_stacksize‘, ‘co_varnames‘]
>>> co.co_names
(‘A‘, ‘f‘, ‘a‘)
>>>

访问PyCodeObject域的py文件:access_demo.py

 1 source = open(‘./demo.py‘).read()
 2 co = compile(source, ‘./demo.py‘, ‘exec‘)
 3
 4 #co = co.co_consts[0]    #code object A
 5 #co = co.co_consts[2]    #code object f
 6
 7 print(‘type(co)             :‘, type(co))
 8 print(‘co.co_argcount       :‘, co.co_argcount)
 9 print(‘co.co_kwonlyargcount :‘, co.co_kwonlyargcount)
10 print(‘co.co_nlocals        :‘, co.co_nlocals)
11 print(‘co.co_stacksize      :‘, co.co_stacksize)
12 print(‘co.co_flags          :‘, co.co_flags)
13 print(‘co.co_code           :‘, co.co_code)
14 print(‘co.co_consts         :‘, co.co_consts)
15 print(‘co.co_names          :‘, co.co_names)
16 print(‘co.co_varnames       :‘, co.co_varnames)
17 print(‘co.co_freevars       :‘, co.co_freevars)
18 print(‘co.co_cellvars       :‘, co.co_cellvars)
19 print(‘co.co_filename       :‘, co.co_filename)
20 print(‘co.co_name           :‘, co.co_name)
21 print(‘co.co_firstlineno    :‘, co.co_firstlineno)
22 print(‘co.co_lnotab         :‘, co.co_lnotab)

运行结果为demo.py文件全局的PyCodeObject的信息:

 1 [[email protected] dis]# python access_demo.py
 2 type(co)             : <class ‘code‘>
 3 co.co_argcount       : 0
 4 co.co_kwonlyargcount : 0
 5 co.co_nlocals        : 0
 6 co.co_stacksize      : 3
 7 co.co_flags          : 64
 8 co.co_code           : b‘Gd\x00\x00d\x01\x00\x84\x00\x00d\x01\x00\x83\x02\x00Z\x00\x00d\x02\x00d\x03\x00\x84\x00\x00Z\x01\x00e\x00\x00\x83\x00\x00Z\x02\x00e\x01\x00\x83\x00\x00\x01d\x04\x00S‘
 9 co.co_consts         : (<code object A at 0x7f471304cb70, file "./demo.py", line 1>, ‘A‘, <code object f at 0x7f4712b66db0, file "./demo.py", line 3>, ‘f‘, None)
10 co.co_names          : (‘A‘, ‘f‘, ‘a‘)
11 co.co_varnames       : ()
12 co.co_freevars       : ()
13 co.co_cellvars       : ()
14 co.co_filename       : ./demo.py
15 co.co_name           : <module>
16 co.co_firstlineno    : 1
17 co.co_lnotab         : b‘\x13\x02\x0c\x02\t\x01‘

去掉access_demo.py第4行的注释:得到类A对应的PyCodeObject

 1 [[email protected] dis]# python access_demo.py
 2 type(co)             : <class ‘code‘>
 3 co.co_argcount       : 1
 4 co.co_kwonlyargcount : 0
 5 co.co_nlocals        : 1
 6 co.co_stacksize      : 1
 7 co.co_flags          : 66
 8 co.co_code           : b‘|\x00\x00Ee\x00\x00Z\x01\x00d\x00\x00Z\x02\x00d\x01\x00S‘
 9 co.co_consts         : (‘A‘, None)
10 co.co_names          : (‘__name__‘, ‘__module__‘, ‘__qualname__‘)
11 co.co_varnames       : (‘__locals__‘,)
12 co.co_freevars       : ()
13 co.co_cellvars       : ()
14 co.co_filename       : ./demo.py
15 co.co_name           : A
16 co.co_firstlineno    : 1
17 co.co_lnotab         : b‘\x10\x01‘

去掉access_demo.py第5行的注释:得到函数f对应的PyCodeObject

 1 [[email protected] dis]# python access_demo.py
 2 type(co)             : <class ‘code‘>
 3 co.co_argcount       : 0
 4 co.co_kwonlyargcount : 0
 5 co.co_nlocals        : 0
 6 co.co_stacksize      : 1
 7 co.co_flags          : 67
 8 co.co_code           : b‘d\x00\x00S‘
 9 co.co_consts         : (None,)
10 co.co_names          : ()
11 co.co_varnames       : ()
12 co.co_freevars       : ()
13 co.co_cellvars       : ()
14 co.co_filename       : ./demo.py
15 co.co_name           : f
16 co.co_firstlineno    : 3
17 co.co_lnotab         : b‘\x00\x01‘

4.pyc文件的生成

根据前面所述,执行普通的.py程序如python demo.py是不会产生pyc的文件,可能因为编译器认为demo.py可能仅运行一次,不会被重用的,所以没有将编译生成PyCodeObject对象存储为pyc文件。

那么如何生成pyc文件呢?

(1)使用import demo

在Python运行的过程中,如果碰到import demo这样的语句,那么Python将在设定好的path中寻找demo.pyc或者demo.dll文件,如果没有找到这些文件,而只是发现了demo.py,然后Python会首先将demo.py编译成相应的PyCodeObject的中间结果,接着创建demo.pyc文件,并将中间结果写入该文件。接下来,Python才会对demo.pyc文件进行import的动作,实际上就是根据demo.pyc文件中记录的编译结果直接建立内存中的PyCodeObject。

 1 [[email protected] dis]# ls
 2 access_demo.py  demo1.py  demo2.py  demo3.py  demo.py  test.py
 3 [[email protected] dis]# python
 4 Python 3.3.0 (default, Nov 21 2012, 11:37:07)
 5 [GCC 4.4.6 20110731 (Red Hat 4.4.6-3)] on linux
 6 Type "help", "copyright", "credits" or "license" for more information.
 7 >>> import demo
 8 >>> exit()
 9 [[email protected] dis]# ls
10 access_demo.py  demo1.py  demo2.py  demo3.py  demo.py  __pycache__  test.py
11 [[email protected] dis]# cd __pycache__/
12 [[email protected] __pycache__]# ls
13 demo.cpython-33.pyc

从上面的例子,可以看出import demo之后,在demo.py所在目录生成了一个__pycache__文件夹,该文件夹下有一个demo.cpython-33.pyc的文件,该pyc文件就是我们想要的。

pyc文件是一个二进制文件。在Windows下可以通过UE查看:

Linux可以通过Vim查看:vim -b demo.cpython-33.pyc,然后输入命令::%!xxd查看,使用:%!xxd -r返回。

pyc文件的文件格式(清楚PyCodeObject中域的含义是了解pyc文件格式的基础)及其具体创建过程,在后续部分会详细介绍。

(2)使用py_compile.compile()

py_compile模块详见:http://docs.python.org/3/library/py_compile.html

The py_compile module provides a function to generate a byte-code file from a source file, and another function used when the module source file is invoked as a script.

py_compile.compile(file, cfile=None, dfile=None, doraise=False, optimize=-1)

Compile a source file to byte-code and write out the byte-code cache file.

The source code is loaded from the file name file.
The byte-code is written to cfile, which defaults to the PEP 3147 path, ending in .pyc (.pyo if optimization is enabled in the current interpreter). For example, if file is/foo/bar/baz.py cfile will default to /foo/bar/__pycache__/baz.cpython-32.pyc for Python 3.2.
 If dfile is specified, it is used as the name of the source file in error messages when instead of file.
If doraise is true, a PyCompileError is raised when an error is encountered while compiling file. Ifdoraise is false (the default), an error string is written to sys.stderr, but no exception is raised.

This function returns the path to byte-compiled file, i.e. whatever cfile value was used.

optimize controls the optimization level and is passed to the built-in compile() function. The default of -1 selects the optimization level of the current interpreter.

 1 [[email protected] dis]# ls
 2 access_demo.py  demo1.py  demo2.py  demo3.py  demo.py  __pycache__  test.py
 3 [[email protected] dis]# rm -rf __pycache__/
 4 [[email protected] dis]# ls
 5 access_demo.py  demo1.py  demo2.py  demo3.py  demo.py  test.py
 6 [[email protected] dis]# python
 7 Python 3.3.0 (default, Nov 21 2012, 11:37:07)
 8 [GCC 4.4.6 20110731 (Red Hat 4.4.6-3)] on linux
 9 Type "help", "copyright", "credits" or "license" for more information.
10 >>> import py_compile
11 >>> py_compile.compile(‘./demo.py‘)
12 ‘./__pycache__/demo.cpython-33.pyc‘
13 >>> exit()
14 [[email protected] dis]# ls
15 access_demo.py  demo1.py  demo2.py  demo3.py  demo.py  __pycache__  test.py
16 [[email protected] dis]# cd __pycache__/
17 [[email protected] __pycache__]# ls
18 demo.cpython-33.pyc

原文地址:https://www.cnblogs.com/Alight/p/9611077.html

时间: 2025-01-07 19:17:52

Python源码中的PyCodeObject的相关文章

《python源码剖析》笔记 Python的编译结果

本文为senlie原创,转载请保留此地址:http://blog.csdn.net/zhengsenlie 1.python的执行过程 1)对python源代码进行编译,产生字节码 2)将编译结果交给python虚拟机,由虚拟机按照顺序一条一条地执行字节码,产生执行结果 图7-1 2.Python编译器的编译结果--PyCodeObject对象 Python编译器的编译结果中包含了字符串.常量值.字节码等在源代码中出现的一切有用的静态信息. 在Python运行期间,这些静态信息被PyCodeOb

《python源码剖析》笔记 Python虚拟机框架

本文为senlie原创,转载请保留此地址:http://blog.csdn.net/zhengsenlie 1. Python虚拟机会从编译得到的PyCodeObject对象中依次读入每一条字节码指令, 并在当前的上下文环境中执行这条字节码指令. Python虚拟机实际上是在模拟操作中执行文件的过程 PyCodeObject对象中包含了字节码指令以及程序的所有静态信息,但没有包含 程序运行时的动态信息--执行环境(PyFrameObject) 2.Python源码中的PyFrameObject

Python源码剖析笔记0 ——C语言基础

python源码剖析笔记0--C语言基础回顾 要分析python源码,C语言的基础不能少,特别是指针和结构体等知识.这篇文章先回顾C语言基础,方便后续代码的阅读. 1 关于ELF文件 linux中的C编译得到的目标文件和可执行文件都是ELF格式的,可执行文件中以segment来划分,目标文件中,我们是以section划分.一个segment包含一个或多个section,通过readelf命令可以看到完整的section和segment信息.看一个栗子: char pear[40]; static

《python源码剖析》笔记 python虚拟机中的函数机制

本文为senlie原创,转载请保留此地址:http://blog.csdn.net/zhengsenlie 1.Python虚拟机在执行函数调用时会动态地创建新的 PyFrameObject对象, 这些PyFrameObject对象之间会形成PyFrameObject对象链,模拟x86平台上运行时栈 2.PyFuctionObject对象 typedef struct { PyObject_HEAD PyObject *func_code: //对应函数编译后的PyCodeObject对象 Py

《python源码剖析》笔记 python虚拟机中的一般表达式

本文为senlie原创,转载请保留此地址:http://blog.csdn.net/zhengsenlie 1.字节码指令 LOAD_CONST:从consts表中读取序号为i的元素并压入到运行时栈中 STORE_NAME:改变local名字空间.从符号表names取序号为i的元素作为变量名, 取运行时栈的栈顶元素作为变量值,完成从变量名到变量值的映射关系的创建. BUILD_MAP:创建一个空的PyDictObject对象,并压入运行时栈 DUP_TOP:将栈顶元素的引用计数增加1,并将它再次

《python源码剖析》笔记 python中的List对象

本文为senlie原创,转载请保留此地址:http://blog.csdn.net/zhengsenlie 1.PyListObject对象 --> 变长可变对象,可看作vector<PyObject *> typedef struct{ PyObject_VAR_HEAD //其中的ob_size表示实际被使用的内存的数量 PyObject **ob_item;//ob_item为指向元素列表的指针,实际上,Python中的list[0]就是ob_item[0] int allocat

《python源码剖析》笔记 python中的Dict对象

本文为senlie原创,转载请保留此地址:http://blog.csdn.net/zhengsenlie 1.PyDictObject对象 -->  C++ STL中的map是基于RB-tree的,搜索时间复杂度是O(logN) PyDictObject采用了hash表,时间复杂度是O(1) typedef struct{ Py_ssize_t me_hash; //me_key的hash值,避免每次查询都要重新计算一遍hash值 PyObject *me_key; PyObject *me_

《python源码剖析》笔记 python中的整数对象

本文为senlie原创,转载请保留此地址:http://blog.csdn.net/zhengsenlie 1. PyIntObject --> long的一个简单包装 typedef struct{ PyObject_HEAD long ob_ival; } PyIntObject; PyInt_Type --> PyIntObject的类型对象.与对象相关的元信息实际上都是保存在与对象对应的类型对象中的 PyTypeObject PyInt_Type = { PyObject_HEAD_I

《python源码剖析》笔记 python中的字符串对象

本文为senlie原创,转载请保留此地址:http://blog.csdn.net/zhengsenlie 1.      PyStringObject --> 变长不可变对象 typedef struct{ PyObject_VAR_HEAD//ob_size变量保存着对象中维护的可变长度内存的大小 longob_shash; //缓存该对象的hash值,用于dict的查询 intob_sstate; //标志该对象是否经过intern机制的处理 char ob_sval[1];// 字符指针