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

随心笔谈2年前发布 admin
186 0 0

文章摘要

这篇文章描述了在PyPy中实现的Py对象字典访问功能,主要实现了一个函数`PyObject_GenericGetAttrWithDict`,用于从对象的字典或其属性中获取属性值。文章详细解释了以下内容: 1. **函数初始化和类型检查**:函数首先获取对象的类型信息,并检查属性名为字符串,否则抛出`TypeError`。随后对属性名进行格式化检查。 2. **描述符查找**:函数尝试从对象的字典或其基类中查找属性值。如果成功找到描述符,并且该描述符定义了`__get__`函数,则调用`__get__`方法获取属性值。 3. **字典查找**:如果未找到描述符或描述符未定义`__get__`方法,则继续从对象的字典中查找属性值。 4. **错误处理**:在无法获取属性值时,函数会抛出`AttributeError`,并根据参数`suppress`决定是否处理错误信息。 5. **PyPy特有实现**:文章重点强调了这段代码是针对PyPy的内核实现,避免了对CPython字典访问的直接访问,从而增强了安全性。 文章通过详细描述函数的执行流程,展示了PyPy中如何通过优化字典访问机制来提高性能。


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;
}

© 版权声明

相关文章