首页 > 代码库 > Python程序执行原理

Python程序执行原理

1.首先在code.h中查看PyCodeObject的struct

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

2.加载模块时,模块对应的PyCodeObject对象会被写入 .pyc中,当然在平时的运行代码的过程中,看不到 .pyc文件,

这个过程必须Python 中库函数compile,具体格式如:compile(source, filename, model[, flags[, dont_inherit]])

其中:

source是字符串或AST(abstract sytnax tree)对象.

filename是文件名,如果不是从文件中读出的代码,可传递一些可辨认的值

model是编译参数,有eval,signal,exec。处理的对象不同

后面参数可选

3.测试

(1)PyCodeObject

文件pycodeobject.py

1 #-*-codeing:UTF-8-*-2 s = "hello the cruel world"3 def func():4     print s5 func()
View Code

在Python交互式shell里编译代码得到PyCodeObject对象:

>>> src = http://www.mamicode.com/open("./code/pycodeobject.py").read()>>> co = compile(src,"pycodeobject.py",exec)>>> dir(co)[__class__, __cmp__, __delattr__, __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_lnotab, co_name, co_names, co_nlocals, co_stacksize, co_varnames]>>> 
View Code

pycodeobjec.py的PyCodeObject对象,print即可

>>> print co.co_argcount0>>> print co.co_nlocals0>>> print co.co_names(s, func)>>> print co.co_varnames()>>> print co.co_consts(hello the cruel world, <code object func at 02118A88, file "pycodeobject.py", line 3>, None)>>> print co.co_coded
View Code

解析指令序列

>>> import dis>>> print dis.dis(co)  2           0 LOAD_CONST               0 (hello the cruel world)              3 STORE_NAME               0 (s)  3           6 LOAD_CONST               1 (<code object func at 02118A88, file "pycodeobject.py", line 3>)              9 MAKE_FUNCTION            0             12 STORE_NAME               1 (func)  5          15 LOAD_NAME                1 (func)             18 CALL_FUNCTION            0             21 POP_TOP                          22 LOAD_CONST               2 (None)             25 RETURN_VALUE        None>>> 
View Code

第一列表示以下几个指令在py文件中的行号;

第二列是该指令在指令序列co_code里的偏移量;

第三列是指令opcode的名称,分为有操作数和无操作数两种,opcode在指令序列中是一个字节的整数;

第四列是操作数oparg,在指令序列中占两个字节,基本都是co_consts或者co_names的下标;

第五列带括号的是操作数说明。

(3)PyFrameObject

在frameobject.h查看PyFrameObject对象的数据结构

 1 typedef struct _frame { 2     PyObject_VAR_HEAD 3     struct _frame *f_back;    /* previous frame, or NULL */ 4     PyCodeObject *f_code;    /* code segment */ 5     PyObject *f_builtins;    /* builtin symbol table (PyDictObject) */ 6     PyObject *f_globals;    /* global symbol table (PyDictObject) */ 7     PyObject *f_locals;        /* local symbol table (any mapping) */ 8     PyObject **f_valuestack;    /* points after the last local */ 9     /* Next free slot in f_valuestack.  Frame creation sets to f_valuestack.10        Frame evaluation usually NULLs it, but a frame that yields sets it11        to the current stack top. */12     PyObject **f_stacktop;13     PyObject *f_trace;        /* Trace function */14 15     /* If an exception is raised in this frame, the next three are used to16      * record the exception info (if any) originally in the thread state.  See17      * comments before set_exc_info() -- its not obvious.18      * Invariant:  if _type is NULL, then so are _value and _traceback.19      * Desired invariant:  all three are NULL, or all three are non-NULL.  That20      * one isnt currently true, but "should be".21      */22     PyObject *f_exc_type, *f_exc_value, *f_exc_traceback;23 24     PyThreadState *f_tstate;25     int f_lasti;        /* Last instruction if called */26     /* Call PyFrame_GetLineNumber() instead of reading this field27        directly.  As of 2.3 f_lineno is only valid when tracing is28        active (i.e. when f_trace is set).  At other times we use29        PyCode_Addr2Line to calculate the line from the current30        bytecode index. */31     int f_lineno;        /* Current line number */32     int f_iblock;        /* index in f_blockstack */33     PyTryBlock f_blockstack[CO_MAXBLOCKS]; /* for try and loop blocks */34     PyObject *f_localsplus[1];    /* locals+stack, dynamically sized */35 } PyFrameObject;
View Code

查看栈帧

1 def func():2     import sys3     frame = sys._getframe()4     print frame.f_locals5     print frame.f_globals6     print frame.f_back.f_locals7     #你可以打印frame的各个域8     print s
View Code

 

此次的代码解析来自Python 2.7版本。

Python程序执行原理