正文

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

文章摘要

这篇文章介绍了PyPy中实现的`PyObject_GenericGetAttrWithDict`函数,用于处理Python对象中的属性访问功能。该函数通过Py_TYPE、Py_TYPE_Lookup、PyDict_GetItem等方法,动态地从对象的类或实例中查找属性值。核心逻辑包括: 1. **检查属性名称**:首先验证属性名称是否为字符串,并抛出类型错误。 2. **查找类或实例属性**: - 通过Py_TYPE_Lookup查找属性所在类或实例。 - 如果找到类或实例的`__get__`方法,则调用该方法获取属性值。 - 如果没有找到`__get__`方法,则从对象的`__dict__`中查找。 3. **异常处理**:如果属性缺失,并且不希望抛出异常,则返回`None`。 4. **资源管理**:函数注意对资源的释放,如`PyObject *dict`和`PyObject *desc`。 总结来说,该函数实现了Python对象属性访问的多路处理机制,能够动态地从类或实例中查找属性值,并在需要时抛出异常。

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