python _instance属性_Python虚拟机类机制之instance对象(六)

instance对象中的__dict__

在Python虚拟机类机制之从class对象到instance对象(五)这一章中最后的属性访问算法中,我们看到“a.__dict__”这样的形式。

# 首先寻找'f'对应的descriptor(descriptor在之后会细致剖析)

# 注意:hasattr会在的mro列表中寻找符号'f'

if hasattr(A, 'f'):

descriptor = A.f

type = descriptor.__class__

if hasattr(type, '__get__') and (hasattr(type, '__set__') or 'f' not in a.__dict__):

return type.__get__(descriptor, a, A)

# 通过descriptor访问失败,在instance对象自身__dict__中寻找属性

if 'f' in a.__dict__:

return a.__dict__['f']

# instance对象的__dict__中找不到属性,返回a的基类列表中某个基类里定义的函数

# 注意:这里的descriptor实际上指向了一个普通的函数

if descriptor:

return descriptor.__get__(descriptor, a, A)

在前一章中,我们看到从创建时,Python虚拟机仅为a申请了16个字节的内存,并没有额外创建PyDictObject对象的动作。不过在中,24个字节的前8个字节是PyObject,后8个字节是为两个PyObject *申请的,难道谜底就在这多出的两个PyObject *?

在创建时,我们曾说到,Python虚拟机设置了一个名为tp_dictoffset的域,从名字上判断,这个可能就是instance对象中__dict__的偏移位置。下图1-1展示了我们的猜想:

图1-1   猜想中的a.__dict__

图1-1中,虚线画的dict对象就是我们期望中的a.__dict__。这个猜想可以在PyObject_GenericGetAttr中与上述的伪代码得到证实:

object.c

PyObject * PyObject_GenericGetAttr(PyObject *obj, PyObject *name)

{

PyTypeObject *tp = obj->ob_type;

PyObject *descr = NULL;

PyObject *res = NULL;

descrgetfunc f;

Py_ssize_t dictoffset;

PyObject **dictptr;

……

dictoffset = tp->tp_dictoffset;

if (dictoffset != 0) {

PyObject *dict;

//处理变长对象

if (dictoffset < 0) {

Py_ssize_t tsize;

size_t size;

tsize = ((PyVarObject *)obj)->ob_size;

if (tsize < 0)

tsize = -tsize;

size = _PyObject_VAR_SIZE(tp, tsize);

dictoffset += (long)size;

assert(dictoffset > 0);

assert(dictoffset % SIZEOF_VOID_P == 0);

}

dictptr = (PyObject **) ((char *)obj + dictoffset);

dict = *dictptr;

if (dict != NULL) {

Py_INCREF(dict);

res = PyDict_GetItem(dict, name);

……

}

}

……

}

如果dictoffset小于0,意味着A是继承自str这样的变长对象,Python虚拟机会对dictoffset进行一些处理,最终仍然会使dictoffset指向a的内存额外申请的位置。而PyObject_GenericGetAttr正是根据这个dictoffset获得一个dict对象。更进一步,查看函数g中有设置self(即)中设置的a属性,这个instance对象的属性设置动作也会访问a.__dict__,而且这个动作最终调用的PyObject_GenericSetAttr也是a.__dict__最初被创建的地方:

object.c

int PyObject_GenericSetAttr(PyObject *obj, PyObject *name, PyObject *value)

{

PyTypeObject *tp = obj->ob_type;

PyObject *descr;

……

dictptr = _PyObject_GetDictPtr(obj);

if (dictptr != NULL) {

PyObject *dict = *dictptr;

if (dict == NULL && value != NULL) {

dict = PyDict_New();

if (dict == NULL)

goto done;

*dictptr = dict;

}

……

}

……

}

其中_PyObject_GetDictPtr的代码就是PyObject_GenericGetAttr中根据dictoffset获得dict对象的那段代码

再论descriptor

在上面的伪代码中出现了“descriptor”,这个命名其实是有意为之,目的是唤起前面我们在Python虚拟机类机制之填充tp_dict(二)这一章中所描述过的descriptor。前面我们看到

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值