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 doesn't 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) See
18                    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 s
5 func()
View Code

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

>>> src = 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_argcount
0
>>> print co.co_nlocals
0
>>> 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_code
d
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 it
11        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 to
16      * record the exception info (if any) originally in the thread state.  See
17      * comments before set_exc_info() -- it's 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.  That
20      * one isn't 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 field
27        directly.  As of 2.3 f_lineno is only valid when tracing is
28        active (i.e. when f_trace is set).  At other times we use
29        PyCode_Addr2Line to calculate the line from the current
30        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 sys
3     frame = sys._getframe()
4     print frame.f_locals
5     print frame.f_globals
6     print frame.f_back.f_locals
7     #你可以打印frame的各个域
8     print s
View Code

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

原文地址:https://www.cnblogs.com/sxmcACM/p/4034214.html