python虚拟机之描述器实现原理与源码分析(python在虚拟机里怎么配环境变量)居然可以这样

随心笔谈11个月前发布 admin
82 0


PyObject *
_PyObject_GenericGetAttrWithDict(PyObject *obj, PyObject *name,
PyObject *dict, int suppress)
{

// 首先获取对象的类型 针对于上面的源代码来说就是找到对象 a 的类型
PyTypeObject *tp=Py_TYPE(obj);
PyObject *descr=NULL;
PyObject *res=NULL;
descrgetfunc f;
Py_ssize_t dictoffset;
PyObject **dictptr;

if (!PyUnicode_Check(name)){
PyErr_Format(PyExc_TypeError,
“attribute name must be string, not ‘%.200s'”,
name->ob_type->tp_name);
return NULL;
}
Py_INCREF(name);

if (tp->tp_dict==NULL) {
if (PyType_Ready(tp) < 0)
goto done;
}
// 这个是从所有的基类当中找到一个名字为 name 的对象 如果没有就返回 NULL
// 这里的过程还是比较复杂 需要从类的 mro 序列当中进行查找
descr=_PyType_Lookup(tp, name);

f=NULL;
// 如果找到的类对象不为空 也就是在类本身或者基类当中找到一个名为 name 的对象
if (descr !=NULL) {
Py_INCREF(descr);
// 得到类对象的 __get__ 函数
f=descr->ob_type->tp_descr_get;
// 如果对象有 __get__ 函数则进行进一步判断
if (f !=NULL && PyDescr_IsData(descr)) { // PyDescr_IsData(descr) 这个宏是查看对象是否有 __set__ 函数
// 如果是类对象又有 __get__ 函数 又有 __set__ 函数 则直接调用对象的 __get__ 函数 并且将结果返回
// 这里需要注意一下优先级 这个优先级是最高的 如果一个类对象定义了 __set__ 和 __get__ 函数,那么
// 就会直接调用类对象的 __get__ 函数并且将这个函数的返回值返回
res=f(descr, obj, (PyObject *)obj->ob_type);
if (res==NULL && suppress &&
PyErr_ExceptionMatches(PyExc_AttributeError)) {
PyErr_Clear();
}
goto done;
}
}
// 如果没有名为 name 的类对象 或者虽然有名为 name 的对象 但是只要没有同时定义 __get__ 和 __set__ 函数就需要
// 继续往下执行 从对象本省的 dict 当中寻找
if (dict==NULL) {

// 这部分代码就是从对象 obj 当中找到对象的 __dict__ 字段
dictoffset=tp->tp_dictoffset;
if (dictoffset !=0) {
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);
assert(size <=PY_SSIZE_T_MAX);

dictoffset +=(Py_ssize_t)size;
assert(dictoffset > 0);
assert(dictoffset % SIZEOF_VOID_P==0);
}
dictptr=(PyObject **) ((char *)obj + dictoffset);
dict=*dictptr;
}
}
// 如果对象 obj 存在 __dict__ 字段 那么就返回 __dict__ 字段当中名字等于 name 的对象
if (dict !=NULL) {
Py_INCREF(dict);
res=PyDict_GetItem(dict, name);
if (res !=NULL) {
Py_INCREF(res);
Py_DECREF(dict);
goto done;
}
Py_DECREF(dict);
}
// 如果类对象定义了 __get__ 函数没有定义 __set__ 函数而且在 dict 当中没有找到名为 name 的对象的话
// 那么久调用类对象的 __get__ 函数
if (f !=NULL) {
res=f(descr, obj, (PyObject *)Py_TYPE(obj));
if (res==NULL && suppress &&
PyErr_ExceptionMatches(PyExc_AttributeError)) {
PyErr_Clear();
}
goto done;
}
// 如果类对象没有定义 __get__ 函数那么就直接将这个类对象返回
if (descr !=NULL) {
res=descr;
descr=NULL;
goto done;
}

if (!suppress) {
PyErr_Format(PyExc_AttributeError,
“‘%.50s’ object has no attribute ‘%U'”,
tp->tp_name, name);
}
done:
Py_XDECREF(descr);
Py_DECREF(name);
return res;
}

© 版权声明

相关文章