__getattribute__ 方法和描述符
问题描述
根据本指南关于 python 描述符https://docs.python.org/howto/descriptor.html
according to this guide on python descriptors https://docs.python.org/howto/descriptor.html
新样式类中的方法对象是使用描述符实现的,以避免在属性查找中对它们进行特殊封装.
method objects in new style classes are implemented using descriptors in order to avoid special casing them in attribute lookup.
我理解的方式是有一个方法对象类型实现了 __get__
并在使用实例调用时返回绑定的方法对象,在没有实例且仅调用时返回未绑定的方法对象班级.文章还指出,这个逻辑是在 object.__getattribute__
方法中实现的.像这样:
the way I understand this is that there is a method object type that implements __get__
and returns a bound method object when called with an instance and an unbound method object when called with no instance and only a class. the article also states that this logic is implemented in the object.__getattribute__
method. like so:
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__
本身就是一个方法!那么它如何绑定到一个对象(没有无限递归)?如果它在属性查找中是特殊大小写的,这不会破坏删除旧样式特殊大小写的目的吗?
however object.__getattribute__
is itself a method! so how is it bound to an object (without infinite recursion)? if it is special cased in the attribute lookup does that not defeat the purpose of removing the old style special casing?
解决方案
实际上,在 CPython 中默认的 __getattribute__
实现不是 Python 方法,而是在C. 它可以直接访问对象槽(代表 Python 对象的 C 结构中的条目),而无需通过讨厌的属性访问例程.
Actually, in CPython the default __getattribute__
implementation is not a Python method, but is instead implemented in C. It can access object slots (entries in the C structure representing Python objects) directly, without bothering to go through the pesky attribute access routine.
仅仅因为您的 Python 代码必须这样做,并不意味着 C 代码必须这样做.:-)
Just because your Python code has to do this, doesn't mean the C code has to. :-)
如果你确实实现了 Python __getattribute__
方法,只需使用 object.__getattribute__(self, attrname)
,或者更好的是,super().__getattribute__(attrname)
以访问 self
上的属性.这样你也不会遇到递归.
If you do implement a Python __getattribute__
method, just use object.__getattribute__(self, attrname)
, or better still, super().__getattribute__(attrname)
to access attributes on self
. That way you won't hit recursion either.
在CPython实现中,属性访问实际上是由tp_getattro
槽 在 C 类型对象中,回退到 tp_getattr
slot.
In the CPython implementation, the attribute access is actually handled by the tp_getattro
slot in the C type object, with a fallback to the tp_getattr
slot.
为了详尽并完全公开 C 代码的功能,当您对 实例 使用属性访问时,以下是调用的完整函数集:
To be exhaustive and to fully expose what the C code does, when you use attribute access on an instance, here is the full set of functions called:
Python 将属性访问转换为对 <代码>PyObject_GetAttr() C 函数.该函数的实现查找
tp_getattro
或tp_getattr
插槽.
Python translates attribute access to a call to the
PyObject_GetAttr()
C function. The implementation for that function looks up thetp_getattro
ortp_getattr
slot for your class.
object
类型具有 用 tp_getattro 槽-L1336" rel="nofollow noreferrer">PyObject_GenericGetAttr
函数,它将调用委托给 _PyObject_GenericGetAttrWithDict
(*dict
指针设置为 NULL
和 suppress
参数设置为 0
).这个函数是你的 object.__getattribute__
方法(一个 特殊表名称和插槽之间的映射).
The object
type has filled the tp_getattro
slot with the PyObject_GenericGetAttr
function, which delegates the call to _PyObject_GenericGetAttrWithDict
(with the *dict
pointer set to NULL
and the suppress
argument set to 0
). This function is your object.__getattribute__
method (a special table maps between the name and the slots).
这个_PyObject_GenericGetAttrWithDict
函数可以通过__dict__对象typeobj.html#c.PyTypeObject.tp_dict" rel="nofollow noreferrer">tp_dict
slot,但对于 descriptors(包括方法),_PyType_Lookup
函数.
This _PyObject_GenericGetAttrWithDict
function can access the instance __dict__
object through the tp_dict
slot, but for descriptors (including methods), the _PyType_Lookup
function is used.
_PyType_Lookup
处理缓存并委托给 find_name_in_mro
缓存未命中;后者查找类(和超类)的属性.该代码使用指向 MRO 中每个类上的 tp_dict
槽的直接指针来引用类属性.
_PyType_Lookup
handles caching and delegates to find_name_in_mro
on cache misses; the latter looks up attributes on the class (and superclasses). The code uses direct pointers to the tp_dict
slot on each class in the MRO to reference class attributes.
如果 _PyType_Lookup
找到一个描述符,它会返回到 _PyObject_GenericGetAttrWithDict
并调用该对象上的 tp_descr_get
函数(__get__
钩子).
If a descriptor is found by _PyType_Lookup
it is returned to _PyObject_GenericGetAttrWithDict
and it calls the tp_descr_get
function on that object (the __get__
hook).
当您访问 类本身 上的属性时,而不是 _PyObject_GenericGetAttrWithDict
,type->tp_getattro
插槽由type_getattro()
函数,它也考虑了元类.此版本也调用 __get__
,但将实例参数设置为 None
.
When you access an attribute on the class itself, instead of _PyObject_GenericGetAttrWithDict
, the type->tp_getattro
slot is instead serviced by the type_getattro()
function, which takes metaclasses into account too. This version calls __get__
too, but leaves the instance parameter set to None
.
此代码无需递归调用 __getattribute__
即可访问 __dict__
属性,因为它可以直接访问 C 结构.
Nowhere does this code have to recursively call __getattribute__
to access the __dict__
attribute, as it can simply reach into the C structures directly.
相关文章