Методы метаклассов на экземплярах класса

Мне было интересно, что происходит с методами, объявленными в метаклассе. Я ожидал, что, если вы объявите метод в метаклассе, он станет классом, однако поведение отличается. Пример

>>> class A(object):
...     @classmethod
...     def foo(cls):
...         print "foo"
... 
>>> a=A()
>>> a.foo()
foo
>>> A.foo()
foo

Однако, если я попытаюсь определить метакласс и дать ему метод foo, он, похоже, будет работать одинаково для класса, а не для экземпляра.

>>> class Meta(type): 
...     def foo(self): 
...         print "foo"
... 
>>> class A(object):
...     __metaclass__=Meta
...     def __init__(self):
...         print "hello"
... 
>>> 
>>> a=A()
hello
>>> A.foo()
foo
>>> a.foo()
Traceback (most recent call last):
  File "<stdin>", line 1, in <module>
AttributeError: 'A' object has no attribute 'foo'

Что здесь происходит?

Изменить: наброси вопрос

Ответ 1

Вы подняли хорошую мысль.

Вот хороший справочник, чтобы лучше понять отношения между объектами, классами и метаклассами:

Я также нахожу эту ссылку на дескрипторы достаточно понятной в отношении механизма поиска в python.

Но я не могу сказать, что понимаю, почему a.foo терпит неудачу, когда A.foo добивается успеха. Кажется, что когда вы ищите атрибут объекта, а python не находит его там, он точно не ищет атрибут в классе, потому что если он это сделает, он найдет A.foo.

РЕДАКТИРОВАТЬ:

Ой! Я думаю, что понял. Это связано с тем, как работает наследование. Если вы рассматриваете схему, предоставленную вышеупомянутой ссылкой, она выглядит следующим образом:

alt text

Схематически это сводится к:

type -- object
  |       |
Meta --   A  -- a

Движение влево означает переход в класс данного экземпляра. Идти вверх означает идти к родителю.

Теперь механизм наследования заставляет механизм поиска повернуть направо в приведенной выше схеме. Это идет a → A → object. Это должно быть сделано для того, чтобы следовать правилу наследования! Чтобы было понятно, путь поиска:

 object
   ^
   |
   A  <-- a

Тогда, очевидно, атрибут foo не будет найден.

Однако при поиске атрибута foo в A он обнаруживается, поскольку путь поиска:

type
  ^
  |       
Meta <--   A 

Все это имеет смысл, когда думаешь о том, как работает наследование.

Ответ 2

Правило выглядит так: при поиске атрибута объекта рассматривается класс объекта и его родительские классы. Однако метакласс класса объекта не рассматривается. Когда вы получаете доступ к атрибуту класса, класс класса является метаклассом, поэтому он рассматривается. Откат от объекта к его классу не вызывает "нормальный" поиск атрибутов в классе: например, дескрипторы называются по-разному, доступен ли доступ к экземпляру или его классу.

Способы - это атрибуты, которые вызываются (и имеют метод __get__, который автоматически передает "я" ). Это делает так, что методы метакласса похожи на методы класса, если вы вызываете их в классе, но недоступны на экземпляре.

Ответ 3

Как я понимаю, Meta - это класс, а A - его экземпляр. Таким образом, когда вы вызываете A.foo(), он проверяет объект и его класс. Итак, при попытке A.foo он сначала просматривает те методы, которые сам по себе, а затем методы его класса, Meta. Поскольку A не содержит никакого метода foo, он использует один из Meta и действительно выполняет Meta.foo(A).

Аналогично, когда a.foo проверяется, он сначала просматривает a. Поскольку метод не содержит метода foo, он будет смотреть через A. Но A также не имеет метода foo, поскольку foo находится в Meta. Поскольку ни a, ни A не содержат foo, он вызывает AttributeError.

Я пробовал это как с переменной, так и с функцией, помещая в класс Meta атрибут txt = 'txt', и это также было доступно A, но не a. Итак, я склонен думать, что я прав в своем понимании, но я просто догадываюсь.

Ответ 4

Ссылки в принятом ответе ушли, по крайней мере для меня. Поэтому я хочу сделать некоторые дополнения:

3. Модель данных - объект.__ getattribute__

и дать два ключевых момента на мой взгляд:

  • object.__getattribute__ контроль доступа к атрибуту экземпляра.
  • type.__getattribute__ управления атрибутом класса доступа.