Как работает функция .__ new__ в этом примере? - PullRequest
0 голосов
/ 05 апреля 2020

У меня возникли проблемы с пониманием того, как метод. new magi c создает классы. Ниже приведен пример API, который я использую, который использует метаклассы для динамического создания объектов из информации в базе данных.

class Metaclass(type):
    def __new__(cls, name, bases, dict_):
        print(f' cls - {cls}')
        print(f' name - {name}')
        print(f' bases - {bases}')
        return super(Metaclass, cls).__new__(cls, name, bases, dict_)

class MyObject(object, metaclass=Metaclass):
    pass

class PrimaryObject(MyObject):
    pass

class SecondaryObject(MyObject):
    pass

test = PrimaryObject()

Запуск этого кода вызывает создание экземпляра PrimaryObject, который наследуется от MyObject, который, в свою очередь, наследует от метакласса. Затем вызывается новый метод magi c в метаклассе, который повторяет трижды все объекты, наследуемые от метакласса, даже те, которые не были вызваны:

<class '__main__.Metaclass'>
MyObject
(<class 'object'>,)
<class '__main__.Metaclass'>
PrimaryObject
(<class '__main__.MyObject'>,)
<class '__main__.Metaclass'>
SecondaryObject
(<class '__main__.MyObject'>,)
<__main__.PrimaryObject object at 0x000002214A6786D0>

Каков new метод, получающий эти аргументы, так как ему ничего не передается? Они анализируются с объекта, который его вызывает? И если да, то почему и как он перебирает другие объекты, которые наследуются от объекта, но не являются объектами (SecondaryObject)?

Спасибо

Ответы [ 2 ]

1 голос
/ 07 апреля 2020

"... что, в свою очередь, наследуется от метакласса ..."

Это неверная часть ваших предположений. Ни один из этих классов не «наследуется» от метакласса.

Метакласс используется для их построения - сами классы - только один раз для каждого класса. Вот когда вызывается __new__: когда Python выполняет оператор class (вместе с телом класса).

Создание экземпляра PrimaryObject больше не будет вызывать метакласс __new__ - просто добавьте еще один оператор печати там, перед строкой test = ..., и вы увидите это.

однако, если вы хотите, чтобы метод в метаклассе вызывался при создании экземпляра классов, созданных с метаклассом, то есть метод __call__, а не __new__. Когда вы вызываете super().__call__(...) внутри вашего вызова метакласса, он запускает type.__call__, что, в свою очередь, вызывает методы класса __new__ и __init__, создавая новый экземпляр.

print("defining the metaclass")

class Metaclass(type):
    def __new__(cls, name, bases, dict_):
        print(f' cls - {cls}')
        print(f' name - {name}')
        print(f' bases - {bases}')
        return super(Metaclass, cls).__new__(cls, name, bases, dict_)
    def __call__(cls, *args, **kw):
        print(f"Creating a new instance of {cls.__name__}")
        return super().__call__(*args, **kw)

print("Creating the classes that use the metaclass")

class MyObject(object, metaclass=Metaclass):
    pass

class PrimaryObject(MyObject):
    pass

class SecondaryObject(MyObject):
    pass

print("Creating a class instance")

test = PrimaryObject()
0 голосов
/ 07 апреля 2020

Метакласс - это тип класса. С учетом вашего кода, сравните

>>> type(MyObject)
<class '__main__.Metaclass'>
>>> type(int)
<class 'type'>

В конечном итоге все метаклассы наследуются от type.


. class - это конструкция, используемая для неявного вызова некоторого метакласса. ,

# A = type('A', (), {})
class A:
    pass

# MyObject = Metaclass('MyObject', (), {})
class MyObject(metaclass=Metaclass):
    pass

Как выбран метакласс описано в справочнике по языку Python.


Как и все типы, вызывается сам вызов типа вызов его __new__ метода, поэтому

MyObject = Metaclass('MyObject', (), {})

примерно эквивалентен

MyObject = Metaclass.__new__(Metaclass, 'MyObject', (), {})
if isinstance(MyObject, Metaclass):
    MyObject.__init__()
...