python3.6 源码分析(一)

来源:互联网 发布:ohem算法 编辑:程序博客网 时间:2024/06/06 08:31

从字节码入手

a = 1

编译:

LOAD_CONST               0 (1)STORE_NAME               0 (a)

根据官方文档的字节码解释:
LOAD_CONST从codeobject的co_consts这个列表中读取对应索引的值到栈顶,这里是列表的第0个元素,值为longobject(1)
STORE_NAME将栈顶元素绑定到codeobject的co_names这个列表的第i个元素,这里i是0,也就是stringobject(‘a’)

这时我有一个问题:STORE_NAME后是否弹出了栈顶元素?
在ceval中找到对应代码:

TARGET(STORE_NAME) {            PyObject *name = GETITEM(names, oparg);            PyObject *v = POP();            PyObject *ns = f->f_locals;            int err;            if (ns == NULL) {                PyErr_Format(PyExc_SystemError,                             "no locals found when storing %R", name);                Py_DECREF(v);                goto error;            }            if (PyDict_CheckExact(ns))                err = PyDict_SetItem(ns, name, v);            else                err = PyObject_SetItem(ns, name, v);            Py_DECREF(v);            if (err != 0)                goto error;            DISPATCH();        }

看看TARGET这个宏:

#define TARGET(op) \    case op:

好吧,其实就是一个case。
让我们看一下STORE_NAME做了些啥:
1. 首先获取到要保存的名字
2. 获取要保存的栈顶变量
注意,这里用了POP,猜都能猜到,栈顶已经被删除了。。。。。答案已经找到了,但是我想继续看看后面怎么做
3. 栈帧的locals,也就是局部变量,保存到ns
4. 然后检测ns是否是一个字典,如果是就当作字典,否则就当做一般对象,将名字和对应的值保存到当前栈帧的f_locals,完事!

这里:

if (PyDict_CheckExact(ns))                err = PyDict_SetItem(ns, name, v);            else                err = PyObject_SetItem(ns, name, v);

一般对象的setitem和字典对象的setitem引起了我的兴趣,我决定一探究竟,看看他们有什么区别

首先看PyDict_SetItem:

intPyDict_SetItem(PyObject *op, PyObject *key, PyObject *value){    PyDictObject *mp;    Py_hash_t hash;    if (!PyDict_Check(op)) {        PyErr_BadInternalCall();        return -1;    }    assert(key);    assert(value);    mp = (PyDictObject *)op;    if (!PyUnicode_CheckExact(key) ||        (hash = ((PyASCIIObject *) key)->hash) == -1)    {        hash = PyObject_Hash(key);        if (hash == -1)            return -1;    }    /* insertdict() handles any resizing that might be necessary */    return insertdict(mp, key, hash, value);}

不得不说看python源码就跟看小说一样,一眼就能看清代码的意图,你看一进来先是三个参数的检查,然后计算key的hash值,然后把dict,key,hash,value传给insertdict去进行最后的插入操作,下一步就不去跟进了,要看了字典的具体实现才能理解,毕竟python3.4以后字典做了较大的改动,比较麻烦。

回过头来,再看看PyObject_SetItem的实现:

intPyObject_SetItem(PyObject *o, PyObject *key, PyObject *value){    PyMappingMethods *m;    if (o == NULL || key == NULL || value == NULL) {        null_error();        return -1;    }    m = o->ob_type->tp_as_mapping;    if (m && m->mp_ass_subscript)        return m->mp_ass_subscript(o, key, value);    if (o->ob_type->tp_as_sequence) {        if (PyIndex_Check(key)) {            Py_ssize_t key_value;            key_value = PyNumber_AsSsize_t(key, PyExc_IndexError);            if (key_value == -1 && PyErr_Occurred())                return -1;            return PySequence_SetItem(o, key_value, value);        }        else if (o->ob_type->tp_as_sequence->sq_ass_item) {            type_error("sequence index must be "                       "integer, not '%.200s'", key);            return -1;        }    }    type_error("'%.200s' object does not support item assignment", o);    return -1;}

其中有几个不知道是干嘛的方法
mp_ass_subscript
PySequence_SetItem
不过可以猜到,前者是字典的setitem,而后者是序列对象的setitem,key必须是Py_ssize_t。

源码分析任重而道远,本篇只是一个开头

原创粉丝点击