Python源码剖析[15] —— 最简单的Python实现:Small Python

[绝对原创 转载请注明出处]

Python源码剖析

——Small Python

本文作者: Robert Chen ([email protected] )

1.      Small Python

在详细考察了Python中最常用的几个对象之后,我们现在完全可以利用这些对象做出一个最简单的Python。这一章的目的就是模拟出一个最简单的Python——Small Python

Small Python中,我们首先需要实现之前已经分析过的那些对象,比如PyIntObject,与CPython不同的是,我们并没有实现象CPython那样复杂的机制,作为一个模拟程序,我们只实现了简单的功能,也没有引入对象缓冲池的机制。这一切都是为了简洁而清晰地展示出Python运行时的脉络。

Small Python中,实际上还需要实现Python的运行时环境,Python的运行时环境是我们在以后的章节中将要剖析的重点。在这里只是展示了其核心的思想——利用PyDictObject对象来维护变量名到变量值的映射。

当然,在CPython中,还有许多其他的主题,比如Python源代码的编译,Python字节码的生成和执行等等,在Small Python中,我们都不会涉及,因为到目前为止,我们没有任何资本做出如此逼真的模拟。不过当我们完成这本书的探索之后,就完全有能力实现一个真正的Python了。

Small Python中,我们仅仅实现了PyIntObjectPyStringObject以及PyDictObject对象,仅仅实现了加法运算和输出操作。同时编译的过程也被简化到了极致,因此我们的Small Python只能处理非常受限的表达式。虽然很简陋,但从中可以看到Python的骨架,同时,这也是我们深入Python解释器和运行时的起点。

2.      对象机制

Small Python中,对象机制与CPython完全相同:

[PyObject]

#define PyObject_HEAD /

    int refCount;/

    struct tagPyTypeObject *type

 

 

#define PyObject_HEAD_INIT(typePtr)/

    0, typePtr

 

 

typedef struct tagPyObject

{

    PyObject_HEAD;

}PyObject;

 

 

但是对于类型对象,我们进行了大规模的删减。最终在类型对象中,只定义了加法操作,Hash操作以及输出操作:

[PyTypeObject]

//definition of PyTypeObject

typedef void (*PrintFun)(PyObject* object);

typedef PyObject* (*AddFun)(PyObject* left, PyObject* right);

typedef long (*HashFun)(PyObject* object);

 

 

typedef struct tagPyTypeObject

{

    PyObject_HEAD;

    char* name;

    PrintFun print;

    AddFun add;

    HashFun hash;

}PyTypeObject;

 

 

PyIntObject的实现与CPython几乎是一样的,不过没有复杂的对象缓冲机制:

[PyIntObject]

typedef struct tagPyIntObject

{

    PyObject_HEAD;

    int value;

}PyIntObject;

 

 

PyObject* PyInt_Create(int value)

{

    PyIntObject* object = new PyIntObject;

    object->refCount = 1;

    object->type = &PyInt_Type;

    object->value = value;

    return (PyObject*)object;

}

 

 

static void int_print(PyObject* object)

{

    PyIntObject* intObject = (PyIntObject*)object;

    printf("%d/n", intObject->value);

}

 

 

static PyObject* int_add(PyObject* left, PyObject* right)

{

    PyIntObject* leftInt = (PyIntObject*)left;

    PyIntObject* rightInt = (PyIntObject*)right;

    PyIntObject* result = (PyIntObject*)PyInt_Create(0);

    if(result == NULL)

    {

        printf("We have no enough memory!!");

    }

    else

    {

        result->value = leftInt->value + rightInt->value;

    }

    return (PyObject*)result;

}

 

 

static long int_hash(PyObject* object)

{

    return (long)((PyIntObject*)object)->value;

}

 

 

PyTypeObject PyInt_Type =

{

    PyObject_HEAD_INIT(&PyType_Type),

    "int",

    int_print,

    int_add,

    int_hash

};

 

 

 

 

Small Python中的PyStringObjectCPython中大不相同,在CPython中,它是一个变长对象,而Small Python中只是一个简单的定长对象,因为Small Python的定位就是个演示的程序:

[PyStrObject]

typedef struct tagPyStrObject

{

    PyObject_HEAD;

    int length;

    long hashValue;

    char value[50];

}PyStringObject;

 

 

PyObject* PyStr_Create(const char* value)

{

    PyStringObject* object = new PyStringObject;

    object->refCount = 1;

    object->type = &PyString_Type;

    object->length = (value == NULL) ? 0 : strlen(value);

    object->hashValue = -1;

    memset(object->value, 0, 50);

    if(value != NULL)

    {

        strcpy(object->value, value);

    }

    return (PyObject*)object;

}

 

 

static void string_print(PyObject* object)

{

    PyStringObject* strObject = (PyStringObject*)object;

    printf("%s/n", strObject->value);

}

 

 

static long string_hash(PyObject* object)

{

    PyStringObject* strObject = (PyStringObject*)object;

    register int len;

    register unsigned char *p;

    register long x;

 

 

    if (strObject->hashValue != -1)

        return strObject->hashValue;

    len = strObject->length;

    p = (unsigned char *)strObject->value;

    x = *p << 7;

    while (--len >= 0)

        x = (1000003*x) ^ *p++;

    x ^= strObject->length;

    if (x == -1)

        x = -2;

    strObject->hashValue = x;

    return x;

}

 

 

static PyObject* string_add(PyObject* left, PyObject* right)

{

    PyStringObject* leftStr = (PyStringObject*)left;

    PyStringObject* rightStr = (PyStringObject*)right;

    PyStringObject* result = (PyStringObject*)PyStr_Create(NULL);

    if(result == NULL)

    {

        printf("We have no enough memory!!");

    }

    else

    {

        strcpy(result->value, leftStr->value);

        strcat(result->value, rightStr->value);

    }

    return (PyObject*)result;

}

 

 

PyTypeObject PyString_Type =

{

    PyObject_HEAD_INIT(&PyType_Type),

        "str",

        string_print,

        string_add,

        string_hash

};

 

 

 

 

Python的解释器工作时,还有一个非常重要的对象,PyDictObject对象。PyDictObject对象在Python运行时会维护(变量名,变量值)的映射关系,Python所有的动作都是基于这种映射关系。在Small Python中,我们基于C++中的map来实现PyDictObject对象。当然,map的运行效率比CPython中所采用的hash技术会慢上一些,但是对于我们的Small Pythonmap就足够了:

[PyDictObject]

typedef struct tagPyDictObject

{

    PyObject_HEAD;

    map<long, PyObject*> dict;

}PyDictObject;

 

 

PyObject* PyDict_Create()

{

    //create object

    PyDictObject* object = new PyDictObject;

    object->refCount = 1;

    object->type = &PyDict_Type;

 

 

    return (PyObject*)object;

}

 

 

PyObject* PyDict_GetItem(PyObject* target, PyObject* key)

{

    long keyHashValue = (key->type)->hash(key);

    map<long, PyObject*>& dict = ((PyDictObject*)target)->dict;

    map<long, PyObject*>::iterator it = dict.find(keyHashValue);

    map<long, PyObject*>::iterator end = dict.end();

    if(it == end)

    {

        return NULL;

    }

    return it->second;

}

 

 

int PyDict_SetItem(PyObject* target, PyObject* key, PyObject* value)

{

    long keyHashValue = (key->type)->hash(key);

    PyDictObject* dictObject = (PyDictObject*)target;

    (dictObject->dict)[keyHashValue] = value;

    return 0;

}

 

 

//function for PyDict_Type

static void dict_print(PyObject* object)

{

    PyDictObject* dictObject = (PyDictObject*)object;

    printf("{");

    map<long, PyObject*>::iterator it = (dictObject->dict).begin();

    map<long, PyObject*>::iterator end = (dictObject->dict).end();

    for( ; it != end; ++it)

    {

        //print key

        printf("%d : ", it->first);

        //print value

        PyObject* value = it->second;

        (value->type)->print(value);

        printf(", ");

    }

    printf("}/n");

}

 

 

PyTypeObject PyDict_Type =

{

    PyObject_HEAD_INIT(&PyType_Type),

        "dict",

        dict_print,

        0,

        0

};

Small Python中的对象机制的所有内容都在上边列出了,非常简单,对吧,这就对了,要的就是这个简单。

3.      解释过程

Small Python中没有编译,对的,它根本就不会进行任何常规的编译动作,没有token解析,没有抽象语法树的建立。但说Small Python中有那么一点点编译的味道,其实也不错,我们叫这种动作为解释。无论如何,它至少要解析输入的语句,以判断这条语句到底是要干什么,它是要上山打虎呢,还是要下河摸鱼呢,如果连这最基本的都做不到,Small Python还不如回家卖红薯得了。

然而Small Python中的这种解释动作还是被简化到了极致,它实际上就是简单的字符串查找加if…else…结构:

    void ExcuteCommand(string& command)

    {

        string::size_type pos = 0;

        if((pos = command.find("print ")) != string::npos)

        {

            ExcutePrint(command.substr(6));

        }

        else if((pos = command.find(" = ")) != string::npos)

        {

            string target = command.substr(0, pos);

            string source = command.substr(pos+3);

            ExcuteAdd(target, source);

        }

    }

 

 

 

 

    void ExcuteAdd(string& target, string& source)

    {

        string::size_type pos;

        if(IsSourceAllDigit(source))

        {

            PyObject* intValue = PyInt_Create(atoi(source.c_str()));

            PyObject* key = PyStr_Create(target.c_str());

            PyDict_SetItem(m_LocalEnvironment, key, intValue);

        }

        else if(source.find("/"") != string::npos)

        {

            PyObject* strValue = PyStr_Create(source.substr(1, source.size()-2).c_str());

            PyObject* key = PyStr_Create(target.c_str());

            PyDict_SetItem(m_LocalEnvironment, key, strValue);

        }

        else if((pos = source.find("+")) != string::npos)

        {

            PyObject* leftObject = GetObjectBySymbol(source.substr(0, pos));

            PyObject* rightObject = GetObjectBySymbol(source.substr(pos+1));

            if(leftObject != NULL && right != NULL && leftObject->type == rightObject->type)

            {

                PyObject* resultValue = (leftObject->type)->add(leftObject, rightObject);

                PyObject* key = PyStr_Create(target.c_str());

                PyDict_SetItem(m_LocalEnvironment, key, resultValue);

            }

            (m_LocalEnvironment->type)->print(m_LocalEnvironment);

        }

    }

 

 

 

 

 

 

 通过字符串搜索,如果命令中出现“=”,就是一个赋值或加法过程;如果命令中出现“print”,就是一个输出过程。进一步,在ExcuteAdd中,又进一步进行字符串搜索,以确定是否需要有一个额外的加法过程。根据这些解析的结果进行不同的动作,就是Small Python中的解释过程。这个过程在CPython中是通过正常的编译过程来实现的,而且最后会得到字节码的编译结果。

在这里需要重点指出的是那个m_LocalEnvironment,这是一个PyDictObject对象,其中维护着Small Python运行过程中,动态创建的变量的变量名和变量值的映射。这个就是Small Python中的执行环境,Small Python正是靠它来维护运行过程中的所有变量的状态。在CPython中,运行环境实际上也是这样一个机制。当需要访问变量时,就从这个PyDictObject对象中查找变量的值。这一点在执行输出操作时可以看得很清楚:

    PyObject* GetObjectBySymbol(string& symbol)

    {

        PyObject* key = PyStr_Create(symbol.c_str());

        PyObject* value = PyDict_GetItem(m_LocalEnvironment, key);

        if(value == NULL)

        {

            cout << "[Error] : " << symbol << " is not defined!!" << endl;

            return NULL;

        }

        return value;

    }

 

 

 

 

void ExcutePrint(string symbol)

    {

        PyObject* object = GetObjectFromSymbol(symbol);

        if(object != NULL)

        {

            PyTypeObject* type = object->type;

            type->print(object);

        }

    }

 

 

在这里,通过变量名symbol,获得了变量值object。而在刚才的ExcueteAdd中,我们将变量名和变量值建立了联系,并存放到m_LocalEnvironment中。这种一进一出的机制正是CPython执行时的关键,在以后对Python字节码解释器的详细剖析中,我们将真实而具体地看到这种机制。

4.      交互式环境

好了,我们的Small Python几乎已经完成了,最后所缺的就是一个交互式环境:

char* info = "********** Python Research **********/n";

char* prompt = ">>> ";

 

 

    void Excute()

    {

        cout << info;

        cout << prompt;

        while(getline(cin, m_Command){

            if(m_Command.size() == 0){

                cout << prompt;

                continue;

            }

            else if(m_Command == "exit"){

                return;

            }

            else{

                ExcuteCommand(m_Command);

            }

            cout << prompt;

        }

    }

 

 

有了它,我们的Small Python就大功告成了。现在,来看一看我们的成果吧:

到这里,我们就结束了对Python中对象的探索,通过Small Python这一个简陋的承前启后的东西,我们将敲开Python运行时的大门 :)那里是字节码,解释器,条件判断语句,函数,类,异常等等的神秘世界,提起精神,我们出发了……

你可能感兴趣的:(python,object,String,command,null,iterator)