我有一个关于 的实现的由两部分组成的问题object.__getattribute(self, key),但它们都集中在我对它如何工作的困惑上。
我定义了一个名为 的数据描述符NonNullStringDescriptor,我打算将其附加到属性上。
class NonNullStringDescriptor:
def __init__(self, value: str = "Default Name"):
self.value = value
def __get__(self, instance, owner):
return self.value
def __set__(self, instance, value):
if isinstance(value, str) and len(value.strip()) > 0:
self.value = value
else:
raise TypeError("The value provided is not a non-null string.")
Run Code Online (Sandbox Code Playgroud)
然后我声明一个Person具有 属性的类name。
class NonNullStringDescriptor:
def __init__(self, value: str = "Default Name"):
self.value = value
def __get__(self, instance, owner):
return self.value
def __set__(self, instance, value):
if isinstance(value, str) and len(value.strip()) > 0:
self.value = value
else:
raise TypeError("The value provided is not a non-null string.")
Run Code Online (Sandbox Code Playgroud)
现在,我尝试访问变量属性,一些是描述符,一些是普通实例属性,还有一些不存在:
person = Person()
print("Printing", person.age) # "normal" attribute
print("Printing", person.hobby) # non-existent attribute
print("Printing", person.name) # descriptor attribute
Run Code Online (Sandbox Code Playgroud)
看到的输出是
__getattribute__(age)
Returned 22
Printing 22
__getattribute__(hobby)
__getattr__ invoked.
Printing Unknown
__getattribute__(name)
Returned Default Name
Printing Default Name
Run Code Online (Sandbox Code Playgroud)
我有两个主要问题,两个问题都围绕super(Person, self).__getattribute__(key):
当我尝试访问不存在的属性(例如 )时hobby,我看到它重定向到__getattr__,我知道这通常是属性查找中的“后备”方法。但是,我看到这__getattribute__就是调用此方法的原因。然而,Returned ...控制台输出永远不会被打印,这意味着其余的部分__getattribute__没有完成 - 那么到底如何直接__getattribute__调用__getattr__,返回这个默认的“未知”值而不执行其自己的函数调用的其余部分?
我期望从super(Person, self).__getattribute__(key)( v) 返回的是 的数据描述符实例NonNullStringDescriptor。然而,我看到这v实际上是字符串“默认名称”本身!那么如何object.__getattribute__(self, key)知道使用__get__我的描述符的方法,而不是返回描述符实例呢?
描述符协议中引用了行为:
如果查找的值是定义描述符方法之一的对象,则Python 可能会覆盖默认行为并调用描述符方法。
但它从来没有明确地向我object.__getattribute(self, key)定义执行覆盖时实际发生的情况。我知道最终person.name会转换为低级调用type(person).__dict__["name"].__get__(person, type(person))- 这一切都发生在吗object.__getattribute__?
我发现这篇SO post,它描述了 的正确实现__getattribute__,但我更好奇object.__getattribute__. 但是,我的 IDE (PyCharm) 只为其实现提供了一个存根:
def __getattribute__(self, *args, **kwargs): # real signature unknown
""" Return getattr(self, name). """
pass
Run Code Online (Sandbox Code Playgroud)
__getattribute__不打电话__getattr__。回__getattr__退发生在属性访问机制中,在 __getattribute__引发AttributeError. 如果您想查看实现,请参阅slot_tp_getattr_hookin Objects/typeobject.c。
object.__getattribute__知道调用__get__,因为里面有代码object.__getattribute__调用__get__。这非常简单。如果您想查看实现,请object.__getattribute__在PyObject_GenericGetAttr实现中(是的,即使它说 GetAttr - C 方面的内容与 Python 方面有点不同),并且有两个__get__调用站点(一个用于数据描述符,一个用于数据描述符)非数据描述符),这里和这里。