Наследование с декораторами

Этот вопрос является расширением предыдущего вопроса, который я разместил здесь: ссылка

Я использую Python 3.6.2. У меня есть общий класс Framework от которого мои системы наследуют.

Я пользуюсь декораторами on_initialize, on_event, а также on_finalize информировать Framework где каждый метод должен быть выполнен. Аргумент precedence каждого декоратора используется для определения порядка выполнения для каждого раздела (от низшего к высшему).

# 3 decorators 
def on_initialize(precedence=0):
    def marker(func):
        func._initializer = precedence
        return func
    return marker


def on_event(precedence=0):
    def marker(func):
        func._event_handler = precedence
        return func
    return marker


def on_finalize(precedence=0):
    def marker(func):
        func._finalizer = precedence
        return func
    return marker

# Main framework
class Framework:

    def __init_subclass__(cls, *args, **kw):
        super().__init_subclass__(*args, **kw)
        handlers = dict(_initializer=[], _event_handler=[], _finalizer=[])
        for name, method in cls.__dict__.items():
            for handler_type in handlers:
                if hasattr(method, handler_type):
                    handlers[handler_type].append((getattr(method, handler_type), name))

        for handler_type in handlers:
            setattr(cls, handler_type, 
                    [handler[1] for handler in sorted(handlers[handler_type])])

    def _initialize(self):
        for method_name in self._initializer:
            getattr(self, method_name)()

    def _handle_event(self, event):
        for method_name in self._event_handler:
            getattr(self, method_name)(event)

    def _finalize(self):
        for method_name in self._finalizer:
            getattr(self, method_name)()

    def run(self):
        self._initialize()

        for event in range(10):
            self._handle_event(event)

        self._finalize()


class Recorder(Framework):

    @on_finalize(precedence=0)
    def save_to_db(self):
        print('save_to_db')


class TestFramework(Recorder):

    @on_initialize(precedence=0)
    def get_data(self):
        print('get_data')

    @on_initialize(precedence=1)
    def prepare_data(self):
        print('prepare_data')

    @on_event(precedence=0)
    def process_event(self, event):
        print('process_event', event)

    @on_finalize(precedence=1)
    def generate_report(self):
        print('generate_report')

if __name__ == '__main__':
    tf = TestFramework()
    tf.run()

Результаты, достижения:

> get_data 
> prepare_data 
> process_event 0 
> process_event 1 
> process_event 2
> process_event 3 
> process_event 4 
> process_event 5 
> process_event 6
> process_event 7 
> process_event 8 
> process_event 9 
> generate_report

Метод save_to_db от Recorder не выполняется в TestFramework, Я думаю, что я что-то упускаю в __init_subclass__ где я должен перебирать каждый подкласс. Любая идея? Большое спасибо!

1 ответ

Решение

Вы устанавливаете новый набор обработчиков для каждого класса, игнорируя те из любого базового класса.

Вместо использования пустых списков в handlers словарь, ищите существующие списки и начинайте с них:

def __init_subclass__(cls, *args, **kw):
    super().__init_subclass__(*args, **kw)
    handlers = dict(
        _initializer=getattr(cls, '_initializer', []),
        _event_handler=getattr(cls, '_event_handler', []),
        _finalizer=getattr(cls, '_finalizer', [])))
    # ...

Затем вы должны устранить любые методы, которые переопределены подклассом!

В качестве альтернативы, вместо использования только пространства имен текущего класса (через cls.__dict__), используйте объединенные имена, доступные в dir():

def __init_subclass__(cls, *args, **kw):
    super().__init_subclass__(*args, **kw)
    handlers = dict(_initializer=[], _event_handler=[], _finalizer=[])
    for name in dir(cls):
        method = getattr(cls, name)
        for handler_type in handlers:
            if hasattr(method, handler_type):
                handlers[handler_type].append((getattr(method, handler_type), name))
Другие вопросы по тегам