__getattribute__方法和描述符

8
根据Python描述符指南https://docs.python.org/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

然而,object.__getattribute__本身就是一个方法!那么它是如何绑定到一个对象上的(而不会导致无限递归)?如果在属性查找中特殊处理它,那是否就失去了移除旧式特殊处理的目的?


我理解的方式是有一个方法对象类型,它实现了__get__。 实际上,在这里扮演描述符角色的是function类型(参见https://wiki.python.org/moin/FromFunctionToMethod)。 - bruno desthuilliers
1个回答

16

实际上,在CPython中,默认的__getattribute__实现不是一个Python方法,而是在C中实现的。它可以直接访问对象槽(表示Python对象的C结构中的条目),而不必经过繁琐的属性访问例程。

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

如果你实现了一个Python的__getattribute__方法,只需使用object.__getattribute__(self, attrname),或者更好的方法是使用super().__getattribute__(attrname)来访问self上的属性。这样你就不会遇到递归问题了。

在CPython实现中,属性访问实际上是由C类型对象中的tp_getattro slot处理的,如果失败则回退到tp_getattr slot

为了全面展示C代码的作用,当您在实例上使用属性访问时,以下是调用的完整函数集: 当您访问类本身的属性时,_PyObject_GenericGetAttrWithDict将被替换为type->tp_getattro插槽,该插槽由type_getattro()函数提供服务,该函数也考虑了元类。此版本也调用__get__,但将实例参数设置为None
在这段代码中,没有必要递归调用__getattribute__来访问__dict__属性,因为它可以直接进入C结构。

我不认为我完全理解。你的意思是说__getattribute__不被视为对象的方法,也不像常规方法一样被查找吗?那么谁将其从函数转换为方法呢? - user3848844
@user3848844:object.__getattribute__() 是对内置类 object 的未绑定方法的调用,因此不,它不是通过您所称的常规方法查找的。 - martineau
@user3848844:我已经重新修改了这个内容,使其更加准确。__getattribute__并不是用Python代码实现的,而是一个C函数(在C中没有方法,C不是面向对象的)。由于CPython解释器是用C编写的,因此会调用其他C函数。 - Martijn Pieters
顺便提一下:虽然 hg.python.org 的链接仍然有效,但我想知道是否应该更新为使用 Github CPython。(但那是很多工作...) - torek
@torek:hg.python.org的命运仍然是一个未决问题。目前,它将继续存在。我会根据需要更新答案,指向github视图(通常是当我遇到使用旧链接的答案并收到评论或投票时)。 - Martijn Pieters
我一直在尝试学习有关属性访问的知识,不得不说,您的回答简明扼要且全面。非常感谢! - Colin Hicks

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接