Задать вопрос
@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)
Помогите, пожалуйста, кто знает.
  • Вопрос задан
  • 1459 просмотров
Подписаться 3 Простой 4 комментария
Пригласить эксперта
Ответы на вопрос 1
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>
Ответ написан
Ваш ответ на вопрос

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

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