Это фактически связано с обсуждением здесь на SO.
Краткая версия
def meta(name, bases, class_dict)
return type(name, bases, class_dict)
class Klass(object):
__metaclass__ = meta
meta()
вызывается при выполнении Klass
объявления класса.
Какая часть внутреннего кода (python internal) на самом деле вызывает meta()
?
Длинная версия
Когда класс объявлен, некоторый код должен выполнить соответствующие проверки атрибутов и посмотреть, есть ли __metaclass__
, объявленный в типе. Если такой существует, он должен выполнить вызов метода в этом метаклассе с хорошо известными атрибутами (class_name, bases, class_dict)
. Мне не совсем понятно, какой код отвечает за этот вызов.
Я сделал некоторое копание в CPython (см. ниже), но мне очень хотелось бы иметь что-то ближе к определенному ответу.
Вариант 1: Вызывается напрямую
Метаклассовый вызов жестко связан с разбором классов. Если да, есть ли какие-либо доказательства для этого?
Вариант 2: он вызывается type.__new__()
Код в type_call()
вызывает type_new()
, который в свою очередь вызывает _PyType_CalculateMetaclass()
. Это говорит о том, что разрешение метакласса действительно выполняется во время вызова type()
при попытке выяснить, какое значение нужно вернуть из type()
Это будет соответствовать понятию, что "класс" является "вызываемым, возвращающим объект".
Вариант 3: Что-то другое
Все мои догадки могут быть совершенно неправильными, конечно.
Некоторые примеры, с которыми мы столкнулись в чате:
Пример 1:
class Meta(type):
pass
class A:
__metaclass__ = Meta
A.__class__ == Meta
Это то, что возвращает Meta.__new__()
, поэтому это кажется законным. Метакласс ставит себя как A.__class__
Пример 2:
class Meta(type):
def __new__(cls, class_name, bases, class_dict):
return type(class_name, bases, class_dict)
class A(object):
__metaclass__ = Meta
A.__class__ == type
Изменить 2: исправить исходную версию, правильно вывести Meta
из type
.
Кажется, все в порядке, но я не уверен, что это делает то, что я думаю. Также: Каков канонический метод, чтобы заставить его вести себя, как в примере 1?
Редактирование 3: Использование type.__new__(...)
похоже на работу, как ожидалось, что также кажется в пользу варианта 2.
Может ли кто-нибудь с более глубоким знанием внутренней магии питона просветить меня?
Изменить: A для довольно кратких праймеров на метаклассах: http://blog.ionelmc.ro/2015/02/09/understanding-python-metaclasses/. Также есть некоторые действительно красивые диаграммы, ссылки, а также подчеркивает различия между python 2 и 3.
Изменить 3: для Python 3 есть хороший ответ. Python 3 использует __build_class__
для создания объекта класса. Путь к коду - все равно - другой в Python 2.