Методы метаклассов на экземплярах класса
Мне было интересно, что происходит с методами, объявленными в метаклассе. Я ожидал, что, если вы объявите метод в метаклассе, он станет классом, однако поведение отличается. Пример
>>> 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__
управления атрибутом класса доступа.