методы метаклассов на экземплярах классов - PullRequest
11 голосов
/ 11 февраля 2010

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

>>> 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'

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

edit : натыкаясь на вопрос

Ответы [ 4 ]

15 голосов
/ 11 февраля 2010

Вы подняли хороший вопрос.

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

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

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

EDIT:

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

alt text

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

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

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

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

 object
   ^
   |
   A  <-- a

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

Однако при поиске атрибута foo в A он найден , потому что путь поиска:

type
  ^
  |       
Meta <--   A 

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

8 голосов
/ 11 февраля 2010

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

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

0 голосов
/ 15 июня 2019

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

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

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

  • object.__getattribute__ доступ к атрибуту экземпляра управления.
  • type.__getattribute__ доступ к атрибуту класса управления.
0 голосов
/ 14 февраля 2010

Насколько я понимаю, 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.

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

...