问题描述
根据有关python描述符的本指南 https://docs.python.org/howto/descriptor.html
according to this guide on python descriptorshttps://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实现中,属性访问实际上由 C类型对象中的 tp_getattro
插槽,后退到 tp_getattr
插槽.
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
类型具有用 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
函数可以通过 tp_dict
插槽,但对于描述符(包括方法), _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-&t; 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.
这篇关于__getattribute__方法和描述符的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持!