python3.6 源码分析(一)

从字节码入手

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:

int
PyDict_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的实现:

int
PyObject_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。

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

  • 0
    点赞
  • 2
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值