У меня есть вопрос из двух частей, касающийся реализации 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.")
Затем я объявляю класс Person
с атрибутом name
.
class Person:
name = NonNullStringDescriptor()
def __init__(self):
self.age = 22
def __getattribute__(self, key):
print(f"__getattribute__({key})")
v = super(Person, self).__getattribute__(key)
print(f"Returned {v}")
if hasattr(v, '__get__'):
print("Invoking __get__")
return v.__get__(None, self)
return v
def __getattr__(self, item):
print(f"__getattr__ invoked.")
return "Unknown"
Теперь я пытаюсь получить доступ к переменным атрибутам, некоторые которые являются дескрипторами, некоторыми обычными атрибутами экземпляра и другими несуществующими:
person = Person()
print("Printing", person.age) # "normal" attribute
print("Printing", person.hobby) # non-existent attribute
print("Printing", person.name) # descriptor attribute
Вывод, который мы видим:
__getattribute__(age)
Returned 22
Printing 22
__getattribute__(hobby)
__getattr__ invoked.
Printing Unknown
__getattribute__(name)
Returned Default Name
Printing Default Name
У меня есть два основных вопроса, оба из которых вокруг 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__
?
Я нашел это ТАК сообщение , которое описывает правильное реализация __getattribute__
, но мне более любопытно, что на самом деле происходит в object.__getattribute__
. Тем не менее, моя IDE (PyCharm) предоставляет только заглушку для его реализации:
def __getattribute__(self, *args, **kwargs): # real signature unknown
""" Return getattr(self, name). """
pass