Подтвердить что ты не робот

Кто звонит в метакласс

Это фактически связано с обсуждением здесь на 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.

4b9b3361

Ответ 1

Вы можете найти ответ относительно легко. Сначала найдем код операции для создания класса.

>>> def f():
    class A(object):
        __metaclass__ = type

>>> import dis
>>> dis.dis(f)
  2           0 LOAD_CONST               1 ('A')
              3 LOAD_GLOBAL              0 (object)
              6 BUILD_TUPLE              1
              9 LOAD_CONST               2 (<code object A at 0000000001EBDA30, file "<pyshell#3>", line 2>)
             12 MAKE_FUNCTION            0
             15 CALL_FUNCTION            0
             18 BUILD_CLASS         
             19 STORE_FAST               0 (A)
             22 LOAD_CONST               0 (None)
             25 RETURN_VALUE    

Итак, код операции BUILD_CLASS. Теперь давайте искать источник для этого термина (легко сделать в github mirror).

Вы получите несколько результатов, но наиболее интересным из них является Python/ceval.c, который объявляет функцию static PyObject * build_class(PyObject *, PyObject *, PyObject *); и имеет оператор case для BUILD_CLASS, Поиск по файлу, и вы можете найти определение функции BUILD_CLASS, начиная со строки 4430. И в строке 4456 мы находим бит кода, который вы ищете:

result = PyObject_CallFunctionObjArgs(metaclass, name, bases, methods,
                      NULL);

Таким образом, ответ - это метакласс, который разрешен и вызывается функцией, которая отвечает за выполнение кода операции BUILD_CLASS.

Ответ 2

В Python 3 метакласс вызывается в коде для __build_class__ встроенной функции (которая вызывается для обработки операторов class). Эта функция является новой в Python 3, и эквивалентная функция C build_class в Python 2 не публикуется публично на уровне Python. Однако вы можете найти источник в python/ceval.c

В любом случае, соответствующий вызов объекта metaclass в реализации Python 3 __build_class__:

cls = PyEval_CallObjectWithKeywords(meta, margs, mkw);

Переменная meta - это метакласс (либо type, либо другой метакласс, найденный из аргумента или из типа базового класса). margs - это кортеж с позиционными аргументами (name, bases, dct), а mkw - словарь с аргументами ключевого слова для метакласса (только для Python 3).

Код Python 2 делает нечто похожее:

result = PyObject_CallFunctionObjArgs(metaclass, name, bases, methods,
                                      NULL);

Ответ 3

Мета-классы "создаются" интерпретатором при выполнении определения класса.