#python #python-3.x
#python #python-3.x
Вопрос:
Я пытаюсь более тщательно разобраться в порядке поиска имени дескриптора и явного атрибута.
Я прочитал руководство по дескриптору, и в нем указано следующее:
Детали вызова зависят от того, является ли obj объектом или классом:
…
Для классов механизм находится вtype.__getattribute__
(), который преобразуетB.x
вB.__dict__['x'].__get__(None, B)
Я тестирую это на __class_
_, поскольку это дескриптор данных object
In [47]: object.__class__
Out[47]: type
Таким образом, он возвращает type
ожидаемый результат, поскольку type
class создает все классы, включая object
class . На основе «инструкции по дескриптору», object.__class__
превращается в object.__dict__['__class__'].__get__(None, object)
.
Однако, когда я запускаю его, результатом является сам дескриптор, а не type
In [48]: object.__dict__['__class__'].__get__(None, object)
Out[48]: <attribute '__class__' of 'object' objects>
Я предполагаю, что он возвращает сам дескриптор, потому что внутри этого __get__
есть какой-то код вроде:
if instance is None:
return self
Итак, я понимаю причину возврата самого дескриптора при вызове из класса. Меня смущают разные выходные данные
Когда он говорит « B.x
into B.__dict__['x'].__get__(None, B)
«, я ожидаю, что выходные данные будут одинаковыми. Почему они отличаются?
Ответ №1:
Руководство по описанию — это упрощение. Это умалчивает о таких вещах, как метаклассы, и о том факте, что классы являются объектами. Классы — это объекты, и они проходят как поиск атрибутов «в стиле объекта», так и «в стиле класса», так и обработку дескрипторов. (Реализацию можно найти в type_getattro
, если вы хотите независимо проверить это.)
Поиск object.__class__
не просто выполняется object.__mro__
; он также просматривается type(object).__mro__
. Дескрипторы, найденные в type(object).__mro__
, используют обработку дескриптора в стиле объекта, рассматривая класс как экземпляр своего метакласса, в то время как дескрипторы, найденные в object.__mro__
, используют обработку дескриптора в стиле класса.
Когда вы просматриваете object.__class__
, Python выполняет поиск по type(object).__mro__
. Поскольку object
находится в type(object).__mro__
, этот поиск находит object.__dict__['__class__']
. Поскольку object.__dict__['__class__']
это дескриптор данных (у него есть __set__
), это имеет приоритет перед поиском через object.__mro__
. Таким образом, обрабатывая object
как экземпляр object
, а не как класс, Python выполняет
descr.__get__(object, type(object))
вместо
descr.__get__(None, object)
и __get__
вызов возвращает type(object)
, который является type
.
Ваш descr.__get__(None, object)
вызов вручную обрабатывает object
как класс, а не как экземпляр object
. Вызванный таким образом, дескриптор возвращает сам себя.
Чтобы продемонстрировать, что __class__
здесь не используется специальный случай, мы можем создать наш собственный класс, который является экземпляром самого себя, точно так же, как object
есть:
class DummyMeta(type):
pass
class SelfMeta(type, metaclass=DummyMeta):
@property
def x(self):
return 3
SelfMeta.__class__ = SelfMeta
print(SelfMeta.x)
print(SelfMeta.__dict__['x'].__get__(None, SelfMeta))
print(SelfMeta.__dict__['x'].__get__(SelfMeta, type(SelfMeta)))
Выходной сигнал:
3
<property object at 0x2aff9f04c5e8>
3
Точно так же, как и в object.__class__
, здесь также происходит обработка дескриптора в «объектном стиле». (Также, если вам интересно, свойства являются дескрипторами данных, даже если вы не пишете установщик.)
Комментарии:
1. Таким образом, это означает, что все дескрипторы данных
object
с тех пор всегда проходят через вызов «объектного стиля»object in type(object).__mro__
. И то же самое относится кtype
sincetype in type(type).__mro__
. В конце концов, руководство по описанию просто не упоминает, что вызов «объектного стиля» имеет приоритет над вызовом «классового стиля» (что разрешило бы двусмысленность для классов).2. Поиграв с несколькими примерами class и метакласса, я думаю, что теперь понимаю ваш ответ. Большое спасибо
3. @a_guest @гость: Я думаю, то же самое относится и к
type
. Я проверил , как тамtype.__name__
. Он возвращаетtype
то же самое, что иtype.__dict__['__name__'].__get__(type, type(type))
. Whiletype.__dict__['__name__'].__get__(None, type)
возвращает сам дескриптор.4. Очень элегантная демонстрация. Ключ в том, что
type
является подклассомobject
. Нет специального поиска для,object.__class__
как можно было бы изначально подумать, классов порядка разрешения метода метаклассаtype(object)
, который просматривается как обычно.