Я задавался вопросом, что происходит с методами, объявленными на метаклассе. Я ожидал, что, если Вы объявляете метод на метаклассе, он закончит тем, что был classmethod, однако, поведение отличается. Пример
>>> class A(object):
... @classmethod
... def foo(cls):
... print "foo"
...
>>> a=A()
>>> a.foo()
foo
>>> A.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'
Что продолжается здесь точно?
править: столкновение вопроса
Вы подняли хорошую тему.
Вот хорошая ссылкадля лучшего понимания отношений между объектами, классами и метаклассами:
Я также нахожу эту ссылку по дескрипторам весьма познавательной в отношении механизма поиска в python.
Но я не могу сказать, что понимаю, почему a.foo
не работает, когда A.foo
работает. Похоже, что когда вы ищете атрибут объекта, а python не находит его там, он не совсем ищет атрибут в классе, потому что если бы он это сделал, то нашел бы A.foo
.
EDIT:
О! Кажется, я понял. Это связано с тем, как работает наследование. Если рассмотреть схему, приведенную по ссылке выше, то она выглядит так:
Схематично это сводится к следующему:
type -- object
| |
Meta -- A -- a
Переход влево означает переход к классу данного экземпляра. Переход вверх означает переход к родителю.
Теперь механизм наследования заставляет механизм поиска сделать поворот направо в схеме выше. Он переходит к a → A → object
. Он должен сделать это, чтобы следовать правилу наследования! Чтобы было понятно, путь поиска таков:
object
^
|
A <-- a
Тогда, очевидно, атрибут foo
не будет найден.
Когда вы ищете атрибут foo
в A
, однако, он находится, потому что путь поиска:
type
^
|
Meta <-- A
Все это имеет смысл, если вспомнить, как работает наследование.
Как я понимаю заключается в том, что Meta - это класс, а A - его экземпляр. Таким образом, когда вы вызываете A.foo (), он проверяет объект и его класс. Итак, при попытке A.foo он сначала просматривает методы A сам, а затем методы своего класса Meta. Поскольку A не содержит самого метода foo, он использует метод Meta, и поэтому действительно выполняет Meta.foo (A).
Аналогичным образом, когда a.foo пробуется , он сначала просматривает A. Поскольку a не содержит метода foo, он будет просматривать A. Но A также не содержит метода foo, поскольку foo хранится в Meta. Поскольку ни a, ни A не содержат foo, это вызовет AttributeError.
Я пробовал это остроумие h переменная, а также функция, помещая в класс Meta атрибут txt = 'txt', и это также было доступно для A, но не для a. Итак, я склонен думать, что я прав в своем понимании, но я просто догадываюсь.
Правило таково: при поиске атрибута объекта учитывается класс объекта и его родительские классы. Метакласс класса объекта, однако, не учитывается. Когда вы обращаетесь к атрибуту класса, класс класса является метаклассом, поэтому он учитывается. Возврат от объекта к его классу не вызывает "нормального" поиска атрибута в классе: например, дескрипторы вызываются по-разному, если доступ к атрибуту осуществляется в экземпляре или в его классе.
Методы - это атрибуты, которые могут быть вызваны (и имеют метод __get__
, который заставляет автоматически передавать 'self'). Это делает так, что методы на метаклассе подобны методам класса, если вы вызываете их на классе, но не доступны на экземпляре.