__get__ из дескриптора __class__ класса объектов возвращается не так, как ожидалось

#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 since type in type(type).__mro__ . В конце концов, руководство по описанию просто не упоминает, что вызов «объектного стиля» имеет приоритет над вызовом «классового стиля» (что разрешило бы двусмысленность для классов).

2. Поиграв с несколькими примерами class и метакласса, я думаю, что теперь понимаю ваш ответ. Большое спасибо

3. @a_guest @гость: Я думаю, то же самое относится и к type . Я проверил , как там type.__name__ . Он возвращает type то же самое, что и type.__dict__['__name__'].__get__(type, type(type)) . While type.__dict__['__name__'].__get__(None, type) возвращает сам дескриптор.

4. Очень элегантная демонстрация. Ключ в том, что type является подклассом object . Нет специального поиска для, object.__class__ как можно было бы изначально подумать, классов порядка разрешения метода метакласса type(object) , который просматривается как обычно.