не могу понять фрагмент кода

Изучаю python по Лутцу. Столкнулся с примером кода, логику которого, немогу понять до конца. Пример кода:

def Tracer(classname, supers, classdict):
    print('Tracer')
    aClass = type(classname, supers, classdict)
    class Wrapper:
        print('Wrapper')
        def __init__(self, *args, **kargs):
            print('__init__ Wrapper', args)
            self.wrapped = aClass(*args, **kargs)
        def __getattr__(self, attrname):
            print('Trace', attrname)
            return getattr(self.wrapped, attrname)
    return Wrapper
class Person(metaclass=Tracer):
    print('Person')
    def __init__(self, name, hours, rate):
        print('__init__ Person')
        self.name = name
        self.hours = hours
        self.rate = rate
    def pay(self):
        return self.hours * self.rate
bob = Person('Bob', 40, 50)
#print()
#print(bob.wrapped.name)

Очевидно, что до момента, присвоения классу Person экземпляра(командой bob = Person('Bob', 40, 50)), поток выполнения сначала заходит в Person и выполняет команду print('Person'), после чего в нижней части класса Person неявно выполняется ф-я Tracer(...), которая является метаклассом и возвращает ссылку на класс Wrapper(и выполняет команду print('Wrapper')), т.е. по итогу выполнения Tracer(...) мы получаем неявную ссылку на класс Wrapper, внизу класса Person.

Теперь что мне непонятно.

Почему, после того, как выполняется команда bob = Person('Bob', 40, 50), у нас запускается конструктор Wrapper.__init__(...), а не Person.__init__(...)? Не могу понять как Wrapper.__init__(...) может перехватывать аргументы('Bob', 40, 50), если конструктор Person.__init__(...), расположен в классе Person выше, чем неявная ссылка на Wrapper, которая находиться в самом низу класса Person, а питон читает код сверху-вниз?


Ответы (1 шт):

Автор решения: vitidev

По шагам.

  1. В пространство имен модуля попадает функция Tracer.

  2. Выполнение дошло до определения класса Person и его код выполняется. Каждый def дополняет словарь атрибутов именами методов. Если в классе нет кода, кроме определения методов, то порядок значения не имеет.

  3. По результату выполнения шага 2 получаем имя 'Person' и набор атрибутов. И вместо конструирования класса среда выполнения делегирует эту операцию метаклассу (тому, что указано как метакласс)

  4. Метакласс (в данном случае это функция Tracer) с помощью type() конструирует на основе переданных данных (имя, атрибуты...) класс Person, а также определяет класс Wrapper, где и используется этот класс Person. При этом возвращает класс Wrapper, который и попадает в пространство имен модуля под именем Person.

  5. То есть с этого момента у нас под именем Person на самом деле класс Wrapper и потому Person('Bob', 40, 50) создает инстанс класса Wrapper и вызывает Wrapper.__init__, который пишет в консоль, а затем внутри себя создает экземпляр класса Person и вызывает Person.__init__

→ Ссылка