天天看點

Python随筆(三)虛拟機運作原理 原

說到Python的運作機制,就不得不從.pyc檔案和位元組碼說起 PyCodeObject對象的建立時機是子產品加載的時候,即import。

.pyc檔案

  1. 執行 python test.py 會對test.py進行編譯成位元組碼并解釋執行,但不會生成test.pyc
  2. 如果test.py中加載了其他子產品,如import urllib2,那麼python會對urllib2.py進行編譯成位元組碼,生成urllib2.pyc,然後對位元組碼解釋執行。
  3. 如果想生成test.pyc,我們可以使用python内置子產品py_compile來編譯。也可以執行指令

    python -m py_compile test.py

    這樣,就生成了test.pyc
  4. 加載子產品時,如果同時存在.py和.pyc,python會使用.pyc運作,如果.pyc的編譯時間早于.py的時間,則重新編譯 .py檔案,并更新.pyc檔案。

PyCodeObject

Python代碼的編譯過程就是編譯出PyCodeObject對象 下面是Python3.5.7的PyCodeObject定義

/* Bytecode object */
typedef struct {
    PyObject_HEAD
    int co_argcount;		/* #arguments, except *args CodeBlock中位置參數的個數 */
    int co_kwonlyargcount;	/* #keyword only arguments */
    int co_nlocals;		/* #local variables */
    int co_stacksize;		/* #entries needed for evaluation stack */
    int co_flags;		/* CO_..., see below */
    PyObject *co_code;		/* instruction opcodes */
    PyObject *co_consts;	/* list (constants used) */
    PyObject *co_names;		/* list of strings (names used) */
    PyObject *co_varnames;	/* tuple of strings (local variable names) */
    PyObject *co_freevars;	/* tuple of strings (free variable names) */
    PyObject *co_cellvars;      /* tuple of strings (cell variable names) */
    /* The rest aren't used in either hash or comparisons, except for
       co_name (used in both) and co_firstlineno (used only in
       comparisons).  This is done to preserve the name and line number
       for tracebacks and debuggers; otherwise, constant de-duplication
       would collapse identical functions/lambdas defined on different lines.
    */
    unsigned char *co_cell2arg; /* Maps cell vars which are arguments. */
    PyObject *co_filename;	/* unicode (where it was loaded from) */
    PyObject *co_name;		/* unicode (name, for reference) */
    int co_firstlineno;		/* first source line number */
    PyObject *co_lnotab;	/* string (encoding addr<->lineno mapping) See
				   Objects/lnotab_notes.txt for details. */
    void *co_zombieframe;     /* for optimization only (see frameobject.c) */
    PyObject *co_weakreflist;   /* to support weakrefs to code objects */
} 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 func(a, b, *d, c):
...     m = 1
...     pass
...
>>> func.__code__.co_argcount
>>> func.__code__.co_kwonlyargcount           

複制

  1. co_nlocals:Code Block中的所有局部變量的個數,包括code block的參數(co_argcount+co_kwonlyargcount+可變參數個數)+code block内的局部變量
>>> f1.__code__.co_nlocals
7           

複制

a、b、c、m,4個

  1. co_stacksize:執行該段Code Block需要的棧空間數
>>> f1.__code__.co_stacksize
1           

複制

  1. co_code:Code Block編譯所得的位元組碼指令序列
>>> f1.__code__.co_code
b'd\x01\x00}\x06\x00d\x00\x00S'           

複制

  1. co_consts、co_names
  • co_consts:Code Block中的所有常量的元組
  • co_names:Code Block中的所有符号(名字)的元組
>>> f1.__code__.co_consts
(None, 1)
>>> f1.__code__.co_names
()           

複制

  1. co_filename、co_name
  • co_filename:Code Block所對應的的.py檔案的完整路徑
  • co_name:Code Block的名字,,通常是函數名或類名
>>> f1.__code__.co_filename
'<stdin>'#因為是在控制台裡面是以是stdin
>>> f1.__code__.co_name
'f1'           

複制

  1. co_firstlineno:Code Block在對應的.py檔案中的起始行 test_1.py
def func(a, b, c, *d, e, f):
    m = 1
    pass
print(f1.__code__.co_firstlineno)           

複制

輸出

1           

複制

  1. co_varnames、co_freevars、co_cellvars
  • co_varnames:在本代碼段中被指派,但沒有被内層代碼段引用的變量
  • co_freevars(freevars:自由變量):在本代碼段中被引用,在外層代碼段中被指派的變量
  • co_cellvars(cellvars:被内層代碼所限制的變量):在本代碼段中被指派,且被内層代碼段引用的變量

普通函數代碼段測試

def func(a, b, c, *d, e, f):
    m = 1
    pass

print('co_argcount        :', func.__code__.co_argcount)
print('co_kwonlyargcount  :', func.__code__.co_kwonlyargcount)
print('co_nlocals         :', func.__code__.co_nlocals)
print('co_stacksize       :', func.__code__.co_stacksize)
print('co_flags           :', func.__code__.co_flags)
print('co_code            :', func.__code__.co_code)
print('co_consts          :', func.__code__.co_consts)
print('co_names           :', func.__code__.co_names)
print('co_varnames        :', func.__code__.co_varnames)
print('co_freevars        :', func.__code__.co_freevars)
print('co_cellvars        :', func.__code__.co_cellvars)
print('co_filename        :', func.__code__.co_filename)
print('co_name            :', func.__code__.co_name)
print('co_firstlineno     :', func.__code__.co_firstlineno)
print('co_lnotab          :', func.__code__.co_lnotab)           

複制

輸出

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        : pyvm_test2_function.py
co_name            : func
co_firstlineno     : 1
co_lnotab          : b'\x00\x01\x06\x01'           

複制

Python随筆(三)虛拟機運作原理 原

嵌套函數代碼測試:

def func(a, b, c, *d, e, f):
    m = 1
    def wapper():
        n = m
    print('wapper-->co_argcount        :', wapper.__code__.co_argcount)
    print('wapper-->co_kwonlyargcount  :', wapper.__code__.co_kwonlyargcount)
    print('wapper-->co_nlocals         :', wapper.__code__.co_nlocals)
    print('wapper-->co_stacksize       :', wapper.__code__.co_stacksize)
    print('wapper-->co_flags           :', wapper.__code__.co_flags)
    print('wapper-->co_code            :', wapper.__code__.co_code)
    print('wapper-->co_consts          :', wapper.__code__.co_consts)
    print('wapper-->co_names           :', wapper.__code__.co_names)
    print('wapper-->co_varnames        :', wapper.__code__.co_varnames)
    print('wapper-->co_freevars        :', wapper.__code__.co_freevars)
    print('wapper-->co_cellvars        :', wapper.__code__.co_cellvars)
    print('wapper-->co_filename        :', wapper.__code__.co_filename)
    print('wapper-->co_name            :', wapper.__code__.co_name)
    print('wapper-->co_firstlineno     :', wapper.__code__.co_firstlineno)
    print('wapper-->co_lnotab          :', wapper.__code__.co_lnotab)

print('func-->co_argcount        :', func.__code__.co_argcount)
print('func-->co_kwonlyargcount  :', func.__code__.co_kwonlyargcount)
print('func-->co_nlocals         :', func.__code__.co_nlocals)
print('func-->co_stacksize       :', func.__code__.co_stacksize)
print('func-->co_flags           :', func.__code__.co_flags)
print('func-->co_code            :', func.__code__.co_code)
print('func-->co_consts          :', func.__code__.co_consts)
print('func-->co_names           :', func.__code__.co_names)
print('func-->co_varnames        :', func.__code__.co_varnames)
print('func-->co_freevars        :', func.__code__.co_freevars)
print('func-->co_cellvars        :', func.__code__.co_cellvars)
print('func-->co_filename        :', func.__code__.co_filename)
print('func-->co_name            :', func.__code__.co_name)
print('func-->co_firstlineno     :', func.__code__.co_firstlineno)
print('func-->co_lnotab          :', func.__code__.co_lnotab)
print('=========================================================')
func(1, 2, 3, 4, 5, 6, 7, e = 8, f = 9)           

複制

輸出

Python随筆(三)虛拟機運作原理 原
func-->co_argcount        : 3
func-->co_kwonlyargcount  : 2
func-->co_nlocals         : 7
func-->co_stacksize       : 3
func-->co_flags           : 7
func-->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'
func-->co_consts          : (None, 1, <code object wapper at 0x000002A033189B70, file "pyvm_test3_function.py", line 3>, 'func.<locals>.wapper', 'wapper-->co_argcount        :', 'wapper-->co_kwonlyargcount  :', 'wapper-->co_nlocals         :', 'wapper-->co_stacksize       :', 'wapper-->co_flags           :', 'wapper-->co_code            :', 'wapper-->co_consts          :', 'wapper-->co_names           :', 'wapper-->co_varnames        :', 'wapper-->co_freevars        :', 'wapper-->co_cellvars        :', 'wapper-->co_filename        :', 'wapper-->co_name            :', 'wapper-->co_firstlineno     :', 'wapper-->co_lnotab          :')
func-->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')
func-->co_varnames        : ('a', 'b', 'c', 'e', 'f', 'd', 'wapper')
func-->co_freevars        : ()
func-->co_cellvars        : ('m',)
func-->co_filename        : pyvm_test3_function.py
func-->co_name            : func
func-->co_firstlineno     : 1
func-->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'
=========================================================
wapper-->co_argcount        : 0
wapper-->co_kwonlyargcount  : 0
wapper-->co_nlocals         : 1
wapper-->co_stacksize       : 1
wapper-->co_flags           : 19
wapper-->co_code            : b'\x88\x00\x00}\x00\x00d\x00\x00S'
wapper-->co_consts          : (None,)
wapper-->co_names           : ()
wapper-->co_varnames        : ('n',)
wapper-->co_freevars        : ('m',)
wapper-->co_cellvars        : ()
wapper-->co_filename        : pyvm_test3_function.py
wapper-->co_name            : wapper
wapper-->co_firstlineno     : 3
wapper-->co_lnotab          : b'\x00\x01'           

複制

閉包函數測試:

輸出:

Python随筆(三)虛拟機運作原理 原
func-->co_argcount        : 3
func-->co_kwonlyargcount  : 2
func-->co_nlocals         : 7
func-->co_stacksize       : 3
func-->co_flags           : 7
func-->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'
func-->co_consts          : (None, 1, <code object wapper at 0x0000019920289B70, file "pyvm_test4_function.py", line 3>, 'func.<locals>.wapper')
func-->co_names           : ()
func-->co_varnames        : ('a', 'b', 'c', 'e', 'f', 'd', 'wapper')
func-->co_freevars        : ()
func-->co_cellvars        : ('m',)
func-->co_filename        : pyvm_test4_function.py
func-->co_name            : func
func-->co_firstlineno     : 1
func-->co_lnotab          : b'\x00\x01\x06\x01\x12\x02'
=========================================================
f3-->co_argcount        : 0
f3-->co_kwonlyargcount  : 0
f3-->co_nlocals         : 1
f3-->co_stacksize       : 1
f3-->co_flags           : 19
f3-->co_code            : b'\x88\x00\x00}\x00\x00d\x00\x00S'
f3-->co_consts          : (None,)
f3-->co_names           : ()
f3-->co_varnames        : ('n',)
f3-->co_freevars        : ('m',)
f3-->co_cellvars        : ()
f3-->co_filename        : pyvm_test4_function.py
f3-->co_name            : wapper
f3-->co_firstlineno     : 3
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
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)

參考文獻:

[python虛拟機運作原理]https://www.cnblogs.com/webber1992/p/6597166.html

(adsbygoogle = window.adsbygoogle || []).push({});