Python如何绕过普通属性查找来查找`__dict__`?

时间:2017-10-05 16:44:30

标签: python python-3.x attributes python-internals descriptor

我了解__dict__中的obj.__dict__type(obj)的描述符属性,因此obj.__dict__的查找为type(obj).__dict__['__dict__'].__get__(obj)

来自https://stackoverflow.com/a/46576009

  

很有可能说__dict__必须是描述符,因为   将其作为__dict__条目实现将需要您找到   __dict__之后才能找到__dict__,但 Python已经存在   在查找时绕过常规属性查找以查找__dict__   其他属性,因此不像最初那样引人注目   声音。如果描述符被替换为'__dict__'键   每个__dict____dict__仍然可以找到。

" Python如何绕过正常的属性查找来查找__dict__"? "正常属性查找"意思?

根据链接中引用的上下文,我不认为当作者写这篇文章时,他提到obj.__dict__的查找是type(obj).__dict__['__dict__'].__get__(obj)

1 个答案:

答案 0 :(得分:3)

通过调用__getattribute__ hook或更准确地说C-API tp_getattro slot来完成正常的属性查找。此默认实现位于PyObject_GenericGetAttr C-API function

PyObject_GenericGetAttr的工作是调用描述符(如果它们存在),并查看实例__dict__。实际上,一个__dict__描述符,但__getattribute__直接访问实例内存结构中的__dict__插槽更快,那就是the actual implementation做了什么:

if (dict == NULL) {
    /* Inline _PyObject_GetDictPtr */
    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;
    }
}

请注意内联_PyObject_GetDictPtr 评论;这是性能优化,因为实例属性查找很频繁。

如果您尝试从Python代码访问instance.__dict__,则会调用描述符;它是一个数据描述符对象,因此在查看实例属性之前调用它。