解释器篇
python的虚拟机其实本质上就是模拟cpu执行过程,对应函数栈帧实现Python字节码的执行。
当虚拟机真正执行的时候,其面对的不是PyCodeObject而是P yPyFrameObject
typedef struct _frame{
PyObject_VAR_HEAD
struct _frame *f_back;
PyCodeObject *f_code;
PyObject *f_builtins;
PyObject *f_globals;
PyObject *f_locals;
PyObject **f_valuestack;
PyObject **f_stacktop;
....
int f_lasti;
int f_lineno;
....
//动态内存,维护(局部变量+cell对象集合+free对象集合+运行时栈)所需要的空间
PyObject *f_localsplus[1];
}
运行时候的状态

创建PyFrameObject过程
PyFrameObject *PyFrame_New(PyThreadState *tstate, PyCodeObject *code, PyObject *globals, PyObject *locals){
PyFrameObject *f;
Py_ssize_t extras, ncells, nfree, i;
ncells = PyTuple_GET_SIZE(code->co_cellvals);
nfrees = PyTuple_GET_SIZE(code->co_freevars);
extras = code->code_stack + code->co_nlocals + ncells + nfrees;
f = PyObject_GC_NewVar(PyFrameObject, &PyFrame_Type, extras);
extras = code->con_nlocals + ncells + nfrees;
f->f_valuestack = f->f_localsplus + extras;
f->f_stacktop = f->f_valuestack;
return f;
}

作用域
LEGB
一般分为两种引用方式,属性引用和名字引用,属性引用本质是到名字引用空间中查找一个名字所引用的对象。
解释器执行字节码核心代码
PyObject *PyEval_EvalFrameEx(PyFrameObject *f, int throwflag){
...
why = WHY_NOT;
...
for(;;){
...
fast_next_opcode:
f->f_lasti = INSTR_OFFSET(); //类似PC寄存器的作用
opcode = NEXTOP();
oparg = 0;
if(HAS_ARG(opcode))
oparg = NEXTARG();
dispatch_opcode:
switch(opcode){
case NOP:
goto fast_next_opcode;
case LOAD_FAST:
...
}
}
}
why表明一个字节码指令执行结果。
enum Why_Code {
WHY_NOT = 0x0001
WHY_EXCEPTION = 0x0002
WHY_RERAISE = 0x0004 //在finally中触发异常
WHY_RETURN = 0x0008
WHY_BREAK = 0x0010
WHY_CONTINUE = 0x0020
WHY_YIELD = 0x0040
}
线程进程概念
程序在运行模式分为进程或者线程。这里就会引入一个概念,PyFrameObject本质上是属于某进程或者线程(当然最终肯定是与线程关联)。进程是一个资源的管理单元,线程是一个程序最小运行单元。多个线程共享进程地址空间的全局信息。
Cpu切换任务的时候必须保存线程的运行环境,类似操作系统进程调度。在python中一个线程拥有一个PyThreadState对象,而PyInterpreterState对象代表进程状态对象。
为了节省内存,cpython在实现中让多个线程共享相同的Module对象。一般来说一个python程序拥有一个interpreter对象,一个interpreter对象维护多个PyThreadState,多个Pythreadstate轮流使用字节码解释器。
typedef struct _is {
struct _is *next;
struct _ts *tstate_head;
PyObject *modules;
PyObject *sysdict;
PyObject *builtins;
...
} PyInterpreterState
typedef struct _ts {
struct _ts *next;
PyInterpreterState *interp;
struct _frame *frame;
int recursion_depth;
...
PyObject *dict;
...
long thread_id;
} PyThreadState;
当python虚拟机开始执行的时候,会从frame中取出PyFrameObject设置为当前执行环境,创建新的栈帧则重新设置关系。

字节码与PyFrameObject
比如我们经常用到赋值语句: i = 1,其对应的字节码为:
0 LOAD_CONST 0 (1)
3 STORE_NAME 0 (i)
+对应的字节码
BINARY_ADD
w = POP();
v = TOP();
if(PyInt_CheckExact(v) && PyInt_CheckExact(w)){
register long a, b, i;
a = PyInt_AS_LONG(v);
b = PyInt_AS_LONG(w);
i = a+b;
if((i^a) <0 && (i^b)<0)
goto slow_add;
x = PyInt_FromLong(i);
}
else if (PyString_CheckExact(v) && PyString_CheckExact(w)){
x = string_concatenate(v, w, f, next_instr);
goto skip_decref_vx;
}
else{
slow_add:
x = PyNumber_Add(v, w);
}
Py_DECREF(v);
skip_decref_vx:
Py_DECREF(w);
SET_TOP(x);
break;
逻辑语句的实现
if语句的逻辑比较简单,类似汇编指令里面的jump系列的命令,根据绝对地址或者相对地址找到下一条字节码指令。
for语句会涉及一个特别的字节码SETUP_LOOP
typedef struct _frame{
....
int f_iblock;
PyTryBlock f_blockstack[CO_MAXBLOCKS];
}
void PyFrame_BlockSetup(PyFrameObject *f, int type, int handler, int level){
PyTryBlock *b;
b = &f->f_blockstack[f->f_iblock++];
b->b_type = type;
b->b_level = level;
b->b_handler = handler
}


函数对象
函数对象是在执行字节码的过程生成出来,其本质是包含了作用域和code对象的一个结构。
typedef struct {
PyObject_HEAD
PyObject *func_code;
PyObject *func_globals;
PyObject *func_defaults;
PyObject *func_closure;
PyObject *func_doc;
PyObject *func_name;
PyObject *func_dict;
PyObject *func_weakreflist;
PyObject *func_module;
} PyFunctionObj
PyFunctionObject是python代码在运行时动态产生的,静态信息保存在func_code之中。 对于一段代码而言,PyCodeObject对象只有一个,但PyFunctionObject可以有多个。

MAKE_FUNCTION逻辑
PyObject* PyFunction_New(PyObject *code, PyObject *globals){
PyFunctionObject *op = PyObject_GC_New(PyFunctionObject, &PyFunction_Type);
static PyObject *__name__ = 0;
if(op!=NULL){
...
op->func_code = code;
op->func_globals = globals;
op->func_name = ((PyCodeObject *)code)->co_name;
consts = ((PyCodeObject *)code)->co_consts;
if(PyTuple_size(consts)>=1) {
doc = PyTuple_GetItem(consts, 0);
if(!PyString_Check(doc) && !PyUnicode_Check(doc))
doc = Py_None
}
else
doc = Py_None
...
}
...
}
}
不论CFunction和Method调用都会进入这个call_function
static PyObject* call_function(PyObject ***pp_stack, int oparg){
int na = oparg & 0xff;
int nk = (oparg>>8) & 0xff;
int n = na + 2 * nk; //需要读取的参数个数
PyObject **pfunc = (*pp_stack) -n -1;
PyObject *func = *pfunc;
PyObject *x, *w;
if(PyCFunction_Check(func) && nk == 0){
...
} else {
if (PyMethod_Check(func) && PyMethod_GET_SELF(func) != NULL){
...
}
if(PyFunction_Check(func))
x = fast_function(func, pp_stack, n, na, nk);
else
x = do_call(func, pp_stack, na, nk);
...
}
}
...
return x;
fast_function里面的过程大致就是创建栈帧,然后从ThreadState中取出栈帧添加并调用执行新的栈帧
而且新栈帧的globals来源于之前栈帧的f_globals以及f_locals
函数的局部变量是存放在PyFrameObject的 *f_localsplus对应的空间里面,通过load_fast和store_fast进行操作

创建一个类A对应的字节码
class A(object):
def __init__(self):
c = 10
print c
def b(self):
print "22"

类以及类型


在python中每个对象都有一个type对象,可以通过对象的__class__获取,对于instance的type对应是class对象,而对于class对象的type对应的就是metaclass对象。这个对象在python中对应的就是PyType_Type。
python中每个class对象直接或者间接会与object对象存在is_kind_of关系,type object对应的是PyObject_Type
对于int类型,其找到+对应的方法如下所示:
从PyInt_Type中找到符号表对应的__add__,对应找到函数簇的nb_add。

<type 'type'>对应是的PyType_Type
class ==> PyTypeObject
创建类的过程,为class设置基类关系,然后为其设置类型type,然后初始化dict内容
int PyType_Ready(PyTypeObject *type)
{
PyObject *dict, *bases;
PyTypeObject *base;
Py_ssize_t i, n;
//假设父类不存在以及非PyBaseObject_Type。所以type的父类为object
base = type->tp_base;
if(base == NULL && type != &PyBaseObject_Type){
base = type->tp_base = &PyBaseObject_Type;
}
//该条件判断对象是否初始化完毕
if(base && base->tp_dict == NULL){
PyType_Ready(base)
}
//拷贝父类的类型对象赋值
if (type->ob_type == NULL && base != NULL){
type->ob_type = base->ob_type;
......
}
}

这一步是进行tp_dict的填充

为pylist_Type填充__getitem__

创建类的字节码分析
比如某个类的定义如下
class A(object):
def __init__(self):
pass
def f(self):
pass
1 0 LOAD_CONST 0 ('A')
3 LOAD_NAME 0 (object)
6 BUILD_TUPLE 1
9 LOAD_CONST 1 (<code object A at 0x1073f73b0, file "test.py", line 1>)
12 MAKE_FUNCTION 0
15 CALL_FUNCTION 0
18 BUILD_CLASS
19 STORE_NAME 1 (A)
22 LOAD_CONST 2 (None)
25 RETURN_VALUE
大致的逻辑是加载A,object创建关系继承列表,加载code对象,创建一个function,然后调用function。function对应的内容其实就是函数code的内容。
python引擎在执行call_function其实对应是执行两次def函数,创建两个FunctionObject对象。

PyObject * build_class(PyObject *methods, PyObject *bases, PyObject *name){
PyObject *metaclass = NULL, *result, *base;
if(PyDict_check(methods))
metaclass = PyDict_GetItemString(methods, "__metaclass__");
if (metaclass != NULL)
Py_INCRE(metaclass);
else if (PyTuple_checks(bases) && PyTuple_Get_Size(bases)>0){
base = PyTuple_Get_ITEM(bases, 0);
metaclass = PyObject_GetAttrString(bases, "__class__");
}else{
....
}
result = PyObject_CallFunctionObjArgs(metaclass, name, bases, methods, NULL);
.....
return result;
}
查看元类是否定义,没有则从父类第一个提取出__class__作为metaclass。
类和instance的__new__的差别

python的instance和class都拥有__dict__,因此不同对象可以设置不同的内容,不同的calss可以设置不同的内容。