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)

运行:

[root@lq 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)

运行:

[root@lq 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)

运行:

[root@lq 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 [root@lq 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 [root@lq 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 [root@lq 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 [root@lq 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 [root@lq 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 [root@lq dis]# ls
2 access_demo.py demo1.py demo2.py demo3.py demo.py test.py
3 [root@lq 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 [root@lq dis]# ls
10 access_demo.py demo1.py demo2.py demo3.py demo.py __pycache__ test.py
11 [root@lq dis]# cd __pycache__/
12 [root@lq __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 [root@lq dis]# ls
2 access_demo.py demo1.py demo2.py demo3.py demo.py __pycache__ test.py
3 [root@lq dis]# rm -rf __pycache__/
4 [root@lq dis]# ls
5 access_demo.py demo1.py demo2.py demo3.py demo.py test.py
6 [root@lq 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 [root@lq dis]# ls
15 access_demo.py demo1.py demo2.py demo3.py demo.py __pycache__ test.py
16 [root@lq dis]# cd __pycache__/
17 [root@lq __pycache__]# ls
18 demo.cpython-33.pyc

Python源码中的PyCodeObject的更多相关文章

  1. Python源码读后小结

    Python 笔记 前言(还是叫杂记吧) 在python中一切皆对象, python中的对象体系大致包含了"类型对象", "Mapping对象(dict)", ...

  2. Python 源码剖析(六)【内存管理机制】

    六.内存管理机制 1.内存管理架构 2.小块空间的内存池 3.循环引用的垃圾收集 4.python中的垃圾收集 1.内存管理架构 Python内存管理机制有两套实现,由编译符号PYMALLOC_DEB ...

  3. Python源码剖析|百度网盘免费下载|Python新手入门|Python新手学习资料

    百度网盘免费下载:Python源码剖析|新手免费领取下载 提取码:g78z 目录  · · · · · · 第0章 Python源码剖析——编译Python0.1 Python总体架构0.2 Pyth ...

  4. Python源码剖析——02虚拟机

    <Python源码剖析>笔记 第七章:编译结果 1.大概过程 运行一个Python程序会经历以下几个步骤: 由解释器对源文件(.py)进行编译,得到字节码(.pyc文件) 然后由虚拟机按照 ...

  5. 读python源码--对象模型

    学python的人都知道,python中一切皆是对象,如class生成的对象是对象,class本身也是对象,int是对象,str是对象,dict是对象....所以,我很好奇,python是怎样实现这些 ...

  6. VS2013编译python源码

    系统:win10 手头有个python模块,是用C写的,想编译安装就需要让python调用C编译器.直接编译发现使用的是vc9编译,不支持C99标准(两个槽点:为啥VS2008都还不支持C99?手头这 ...

  7. 三种排序算法python源码——冒泡排序、插入排序、选择排序

    最近在学习python,用python实现几个简单的排序算法,一方面巩固一下数据结构的知识,另一方面加深一下python的简单语法. 冒泡排序算法的思路是对任意两个相邻的数据进行比较,每次将最小和最大 ...

  8. 转换器5:参考Python源码,实现Php代码转Ast并直接运行

    前两个周末写了<手写PHP转Python编译器>的词法,语法分析部分,上个周末卡文了. 访问器部分写了两次都不满意,没办法,只好停下来,参考一下Python的实现.我实现的部分正好和Pyt ...

  9. python源码书籍

    <Python源码剖析>一书现在很难买到,目前大部分都是电子书. 为了更好地利用Python语言,无论是使用Python语言本身,还是将Python与C/C++交互使用,深刻理解Pytho ...

随机推荐

  1. python数据结构之队列(一)

    队列概念 队列(queue)是只允许在一端进行插入操作,而在另一端进行删除操作的线性表. 队列是一种先进先出的(First In First Out)的线性表,简称FIFO.允许插入的一端为队尾,允许 ...

  2. BZOJ-6-2460: [BeiJing2011]元素-线性基

    链接 :https://www.lydsy.com/JudgeOnline/problem.php?id=2460 思路 :线性基不唯一,所以排序 进行贪心选择,价值最大的线性基, #include& ...

  3. Java的运算符

    运算符用于执行程序代码运算,会针对一个以上操作数项目来进行运算.下面介绍JAVA中的运算符: (1)算术运算符: 单目:+(取正) -(取负) ++(自增1) --(自减1) 双目:+ - * / % ...

  4. 负载均衡---ribbon

    Ribbon:提供云端负载均衡,有多种负载均衡策略可供选择,可配合服务发现和断路器使用. 上一篇简单讲解了eureka的使用,这一篇文章基于上一篇的基础上,讲一下springcloud的另一个重要的组 ...

  5. 多表查询、可视化工具、pymysql模块

    create table dep( id int primary key auto_increment, name varchar(16), work varchar(16) ); create ta ...

  6. 给有C或C++基础的Python入门 :Python Crash Course 1 - 3

    暑假ACM集训结束,预习下个学期要学习的Python. 前几章比较基础,玩玩学学很快学完了,这里随意写点收获. 第一章 搭建编译环境   用的是最新的Python3.编译器用的是推荐的Geany..具 ...

  7. MySQL firstmatch strategy

    在探讨subquery如 x IN (SELECT XX FROM TABLE)这样的形式的MATCH策略时,不是很清楚实现过程.在网上搜了一下, 地址:http://stackoverflow.co ...

  8. 【ACM】 1231 最大连续子序列

    [1231 最大连续子序列 ** Time Limit: 2000/1000 MS (Java/Others) Memory Limit: 65536/32768 K (Java/Others) To ...

  9. java中打开文件流后要关闭后重新打开

    记录错误,打开文件流一定要关闭并重新打开文件流,不然取的文件内容永远是第一次取的文件内容: /** * 读取配置文件 */ private Properties readProperties() { ...

  10. 全国青少年信息学奥林匹克分区联赛(N)竞赛大纲

    全国青少年信息学(计算机)奥林匹克分区联赛竞赛大纲 一.初赛内容与要求:(#表示普及组不涉及,以下同) 计算机的基本发展 诞生与发展 特点 在现代社会中的应用 计算机系统的基本组成 计算机的工作原理# ...