我一直在深入查阅 Python 文档中的描述符,但我无法理解其中关于描述符调用和给定 Python 等价于
object.__getattribute__()
. 的一些要点
给出的纯 Python 等价物(请参见下面代码中的“object_getattribute”)假定通过调用 __getattribute__()
调用
getattr()
的 type版本,其中类对象作为第一个参数提供,这导致调用 type 的
__getattribute__()
。问题是,让 type 的属性查找(在 type.__getattribute__()
中实现)参与实例调用的查找应该会把事情搞砸,因为它会桥接到不同的关系线,其中类是对象,元类是类.当类的 MRO耗尽时,由
object.__getattribute__()
进行的查找搜索应该终止(此时将使用 getattr()),但是调用 __getattribute__()
的 type 将不合逻辑地级联搜索到覆盖元类的 dict 并可能取回一个类似名称的属性,这是错误的,因为元类定义的是类,而不是对象。
下面是一个创建名为“mytype”的元类的示例,一个名为“base”的基类使用 Python 等效项提供于:https://docs.python.org/3/howto/descriptor.html#technical-tutorial ,和一个实例类,“myclass”,来自“mytype”:
class mytype(type):
cls_var = 11
class base:
def object_getattribute(obj, name):
null = object()
objtype = type(obj)
cls_var = getattr(objtype, name, null)
descr_get = getattr(type(cls_var), '__get__', null)
if descr_get is not null:
if (hasattr(type(cls_var), '__set__') or hasattr(type(cls_var), '__delete__')):
return descr_get(cls_var, obj, objtype)
if hasattr(obj, '__dict__') and name in vars(obj):
return vars(obj [name]
if descr_get is not null:
return descr_get(cls_var, obj, objtype)
if cls_var is not null:
return cls_var
raise AttributeError(name)
def __init__(s, v):
s.core = v
@property
def core(s):
return '-- ' + s._core + ' --'
@core.setter
def core(s, v):
s._core = v
myclass = mytype('myclass', (base, ), {'a':97})
o = myclass('IRON')
(注意:我不知道如何在创建类时将方法定义添加到类中,抛出调用 type 的 new(),所以我想出了类“base”作为提供“object_getattribute”的方法()”文档中的方法)
现在,如果我跑:
print(o.__getattribute__('cls_var'))
或者,更简单地说:
print(o.cls_var
我得到:
...
AttributeError: 'myclass' object has no attribute 'cls_var'
[Program finished]
结果是我通常期望的。搜索不在元类中查找以评估名称 - 这是不同的领域。但是,如果我使用文档中的纯 Python 等效项,如下所示:
print(o.object_getattribute('cls_var'))
然后我得到:
11
哪个在mytype.
总结:文档中提供的纯Python版本,错了吗?
文档中的示例不正确。
如果你现在看那里 -
https://docs.python.org/3/howto/descriptor.html#technical-tutorial
你会看到在类中获取名称,检查它是否是一个描述符,不使用 type(obj).__getattribute__
- 相反,他们写了一个辅助函数 find_name_in_mro
它准确地模拟了检索属性的机制。
当打开旧 Python 版本的相同 URL 时,例如 https://docs.python.org/3.9/howto/descriptor.html#technical-tutorial,您会注意到粘贴在问题 - 事实上,这是不正确的。
所以,换句话说,它有可能让最终的读者更清楚地了解事情: 在 Python 中检索 any 实例中的属性时,会调用该实例的
__getattribute__
。通常这将默认为object.__getattribute__
,它的作用是:
__getattribute__
- 它是自定义搜索,它将在类'__dict__
中搜索属性,然后在其所有线性化超类的__dict__
中搜索(mro - “方法决议顺序)到“对象”。
__set__
或 __del__
特征的对象。此查找是非递归的,并直接检查(检索到的对象的)类slots
-“非数据描述符”存在这种差异,因为-描述符仅具有__get__
,因为它们可以被实例中设置的值遮蔽。数据描述符永远不会被实例中的值遮蔽。__get__
,其返回值用作__getattribute__
__dict__
本身。请注意,如果在类定义中使用了__dict__
,则用户定义类中的属性可以存在于其实例没有__slots__
的类中。然而,__slots__
中的属性访问本身是通过专门的描述符执行的——因此,Python 将使用上面的案例 1.1。__dict__
被返回__get__
。这种情况用于检索包装在绑定方法中的类中的函数(函数中的.__get__()
方法这样做)。__getattr__
在同一实例中被调用 - 如果已定义 - 它的返回值,或 AttributeError
异常被用作 __getattribute__
AttributeError
被提出来。