Задать вопрос
@Phys_Math_Man

Как устроен вызов классов в Python?

Насколько я понял, при вызове класса (в том числе метакласса) cls сначала происходит поиск атрибута __call__ по MRO метакласса, экземпляром которого является cls, и далее этот атрибут вызывается соответствующим образом (в зависимости от того, является ли он функцией, экземпляром дескриптора и т. д.) - поправьте, если это не так. Теперь остается разобраться с семантикой вызовов
type.__call__.__get__(cls)(*args, **kwargs)
type.__init__.__get__(cls)(*args, **kwargs)
type.__new__(cls, *args, **kwargs)
object.__new__(cls, *args, **kwargs)
object.__init__.__get__(cls)(*args, **kwargs)
Помогите, пожалуйста, кто знает.
  • Вопрос задан
  • 1121 просмотр
Подписаться 3 Простой 4 комментария
Пригласить эксперта
Ответы на вопрос 2
Vindicar
@Vindicar
RTFM!
Насколько я это понимаю:
1. Вызов класса транслируется в обращение к метаклассу, т.е. klass.__class__.__call__()
2. По умолчанию метакласс обращается к klass.__new__(). Если класс не определяет этот метод, он ищется по предкам. Задача __new__() - вернуть экземпляр класса, который был "сконструирован". Это не обязательно новый экземпляр, у нас может быть синглтон или ещё что-то.
3. Получив экземпляр instance, метакласс обращается к instance.__class__.__init__(), чтобы проинициализировать возвращённый экземпляр. Вроде где-то упоминалось, что если __new__() возвращает экземпляр другого класса, то и __init__() будет вызван от этого другого класса.
4. После того, как экземпляр был проинициализирован, klass.__class__.__call__() его возвращает программе
Это подтверждается таким тестовым кодом:
class MetaTest(type):
    def __call__(self, *args, **kwargs):
        print('MetaTest.__call__() is being called...')
        instance = super().__call__(*args, **kwargs)
        print(f'MetaTest.__call__() returning {instance=}')
        return instance

class Test(metaclass=MetaTest):
    def __new__(cls):
        print('Test.__new__() is being called...')
        instance = super().__new__(cls)
        print(f'Test.__new__() returning {instance=}')
        return instance
    
    def __init__(self):
        print(f'Test.__init__() has been called on instance = {self}')


t = Test()

И вот результат выполнения:
MetaTest.__call__() is being called...
Test.__new__() is being called...
Test.__new__() returning instance=<__main__.Test object at 0x0000028EC8E41700>
Test.__init__() has been called on instance = <__main__.Test object at 0x0000028EC8E41700>
MetaTest.__call__() returning instance=<__main__.Test object at 0x0000028EC8E41700>
Ответ написан
Lord_of_Rings
@Lord_of_Rings Куратор тега Python
Дунадан - северный странник. Злой, но очень добрый
Действительно, чёт не очень понятен вопрос. Это всё описывается примерно так:

1. Когда вызывается класс, интерпретатор ищет __call__ в метаклассе этого класса. По умолчанию метакласс — type. Этот type.__call__ управляет процессом создания экземпляра. Если метакласс переопределен, его __call__ будет использоваться вместо type.__call__.
2. type.__call__ вызывает __new__ класса
3. Если класс не переопределил __new__ type.__call__ вызывает __init__ класса. Если переопределен и возвращает объект другого типа, __init__ не вызывается, если того же - вызывается

Например, такой код
class Test:
    def __new__(cls, *args, **kwargs):
        print("__new__")
        return super().__new__(cls)

    def __init__(self):
        print("__init__")

test = Test()
выведет
__new__
__init__

А такой
class Test:
    def __new__(cls, *args, **kwargs):
        print("__new__")
        return 'test'

    def __init__(self):
        print("__init__")

test = Test()
только __new__

P. S. Ну а вообще вам полезно вот это почитать
Ответ написан
Ваш ответ на вопрос

Войдите, чтобы написать ответ

Похожие вопросы