Подтвердить что ты не робот

Как работает процесс поиска атрибутов python?

Когда я говорю "процесс поиска атрибутов python", я имею в виду: что делает python, когда вы пишете x.foo??

Поиск в Интернете я не нашел много документов об этом, одна из лучших статей, которые я нашел, возобновил процесс до следующих шагов (вы можете увидеть полную статью здесь)

  • Если attrname является специальным (то есть Python-предоставленным) атрибутом для objectname, верните его.
  • Проверить имя объекта.__ class __.__ dict__ для attrname. Если он существует и является дескриптором данных, верните результат дескриптора. Поиск всех оснований objectname.__ class__ для одного и того же случая.
  • Проверьте имя объекта.__ dict__ для имени attrname и верните, если он найден. Если objectname является классом, ищите его базы тоже. Если это класс и существует дескриптор в нем или его основаниях, верните результат дескриптора.
  • Проверить имя объекта.__ class __.__ dict__ для attrname. Если он существует и является дескриптором без данных, верните результат дескриптора. Если он существует и не является дескриптором, просто верните его. Если он существует и является дескриптором данных, мы не должны быть здесь, потому что мы вернемся в точке 2. Найдите все базы objectname.__ class__ для одного и того же случая.
  • Raise AttributeError.

Сначала это может показаться правильным, но процесс поиска атрибутов немного сложнее, например, для x.foo, он не ведет себя одинаково, если x является классом или экземпляром.

У меня есть несколько примеров, которые невозможно объяснить этим способом. Рассмотрим следующий код python:

class Meta(type):
    def __getattribute__(self, name):
        print("Metaclass getattribute invoked:", self)
        return type.__getattribute__(self, name)

    def __getattr__(self, item):
        print('Metaclass getattr invoked: ', item)
        return None

class C(object, metaclass=Meta):
    def __getattribute__(self, name):
        print("Class getattribute invoked:", args)
        return object.__getattribute__(self, name)

c=C()

Теперь проверьте следующие строки с соответствующим выходом:

>> C.__new__
Metaclass getattribute invoked: <class '__main__.C'>
<built-in method __new__ of type object at 0x1E1B80B0>

>> C.__getattribute__
Metaclass getattribute invoked: <class '__main__.C'>
<function __getattribute__ at 0x01457F18>

>> C.xyz
Metaclass getattribute invoked: <class '__main__.C'>
Metaclass getattr invoked:  xyz
None

>> c.__new__
Class getattribute invoked: (<__main__.C object at 0x013E7550>, '__new__')
<built-in method __new__ of type object at 0x1E1B80B0>

>> c.__getattribute__
Class getattribute invoked: (<__main__.C object at 0x01438DB0>, '__getattribute__')
Metaclass getattribute invoked: <class '__main__.C'>
<bound method C.__getattribute__ of <__main__.C object at 0x01438DB0>>

>> 

Выводы, которые я сделал (учитывая, что мы ищем x.foo):

  • __ getattribute__ отличается для экземпляров < тип 'type' > и < type 'object' > . Для C.foo(), "foo" выполняется поиск сначала на C.__ dict__ и возвращается, если найден (вместо поиска типа (C)), а для x.foo() 'foo' выполняется поиск по типу (x).__ dict__ и на x.__ dict __.
  • __ Метод getattribute__ всегда разрешен по типу (x), что я не понимаю здесь, это последний случай: c.__ getattribute__, is not object содержит метод __getattribute__ (и C наследует от объекта), так почему метакласс Получается метод getattribute.

Может кто-нибудь объяснить это, пожалуйста? или меньше расскажите мне, где я могу найти документацию об этом, спасибо.

4b9b3361

Ответ 1

Если вы добавили print("Metaclass getattribute invoked:", self, name), вы увидите:

>>> c.__getattribute__
Class getattribute invoked: <__main__.C object at 0x2acdbb1430d0> __getattribute__
Metaclass getattribute invoked: <class '__main__.C'> __name__
<bound method C.__getattribute__ of <__main__.C object at 0x2acdbb1430d0>>

Метакласс __getattribute__ вызывается, чтобы построить repr выражения c.__getattribute__, чтобы он мог печатать C __name__.

btw, __getattribute__ работает одинаково для классов и метаклассов; атрибут сначала открывается в экземпляре, а затем в типе экземпляра.

>>> Meta.foo = 1
>>> C.foo
('Metaclass getattribute invoked:', <class '__main__.C'>, 'foo')
1
>>> c.foo
('Class getattribute invoked:', <__main__.C object at 0x2acdbb1430d0>, 'foo')
Traceback (most recent call last):
  File "<stdin>", line 1, in <module>
  File "<stdin>", line 5, in __getattribute__
AttributeError: 'C' object has no attribute 'foo'
>>> C.bar = 2
>>> c.bar
('Class getattribute invoked:', <__main__.C object at 0x2acdbb1430d0>, 'bar')
2