1. 程式人生 > >Python源碼中的PyCodeObject

Python源碼中的PyCodeObject

映射 row pairs number weak spa evel clu 應該

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