Наследование с декораторами
Этот вопрос является расширением предыдущего вопроса, который я разместил здесь: ссылка
Я использую 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))