Python:__ getattribute__方法和描述符

use*_*844 3 python attributes python-internals

根据本指南python描述符 https://docs.python.org/2/howto/descriptor.html

新样式类中的方法对象是使用描述符实现的,以避免在属性查找中特殊包装它们.

我理解这一点的方法是有一个方法对象类型实现__get__并在使用实例和未绑定的方法对象调用时返回绑定的方法对象,在没有实例和只有类的情况下调用.文章还指出,这个逻辑是在object .__ getattribute__方法中实现的.像这样:

def __getattribute__(self, key):
    "Emulate type_getattro() in Objects/typeobject.c"
    v = object.__getattribute__(self, key)
    if hasattr(v, '__get__'):
       return v.__get__(None, self)
    return v
Run Code Online (Sandbox Code Playgroud)

但是对象.__ getattribute__本身就是一种方法!那怎么绑定一个对象(没有无限递归)?如果它在属性查找中是特殊的,那不会破坏删除旧式特殊套管的目的吗?

Mar*_*ers 8

实际上,在CPython中,默认__getattribute__实现不是Python方法,而是在C中实现.它可以直接访问对象槽(C结构中表示Python对象的条目),而无需经历麻烦的属性访问例程.

仅仅因为你的Python代码必须这样做,并不意味着C代码必须这样做.:-)

如果您实现了Python __getattribute__方法,只需使用object.__getattribute__(self, attrname)或更好super(YourClassName, self).__getattribute__(attrname)地访问属性self.这样你就不会遇到递归.

在CPython实现中,属性访问实际上由C类型对象中的tp_getattro插槽处理,并回退到tp_getattr插槽.这样你就可以避免递归.

为了详尽无遗并完全暴露C代码的作用,当您在实例上使用属性访问时,这里是完整的函数集:

  • Python将属性访问转换为对PyObject_GetAttr()C函数的调用.该函数实现会查找您的类的槽tp_getattrotp_getattr槽.

  • object类型已tp_getattr填充_PyObject_GenericGetAttrWithDict功能.此函数是您的object.__getattribute__方法(名称和插槽之间的特殊表映射).

  • 此函数可以__dict__通过tp_dict插槽访问实例对象,但对于描述符(包括方法),使用该_PyType_Lookup函数.

  • _PyType_Lookup查找类(和超类)上的属性.该代码使用直接指针cl_dict(自定义Python类)并tp_dict引用类和类型字典.

  • 如果找到一个描述符_PyType_Lookup,则返回_PyObject_GenericGetAttrWithDict它并调用该tp_descr_get对象上的函数(__get__钩子).

当您访问类本身的属性,而不是_PyObject_GenericGetAttrWithDict,该type->tp_getattro插槽改为由服务type_getattro()功能,这需要元类进去了.此版本也调用__get__,但将实例参数设置为None.

这段代码无法以递归方式调用__getattribute__访问__dict__属性,因为它可以直接进入C结构.