跟进 Dutch Master 的 AOP 参考资料,您会发现当您开始添加参数以修改被修饰函数/方法的行为时,使用装饰器变得特别有用,并且阅读函数定义上方的内容要容易得多。
我记得在一个项目中,我们需要监督大量的 celery 任务,因此我们想出了使用装饰器根据需要进行插件和调整的想法,类似于:
class tracked_with(object):
"""
Method decorator used to track the results of celery tasks.
"""
def __init__(self, model, unique=False, id_attr='results_id',
log_error=False, raise_error=False):
self.model = model
self.unique = unique
self.id_attr = id_attr
self.log_error = log_error
self.raise_error = raise_error
def __call__(self, fn):
def wrapped(*args, **kwargs):
# Unique passed by parameter has priority above the decorator def
unique = kwargs.get('unique', None)
if unique is not None:
self.unique = unique
if self.unique:
caller = args[0]
pending = self.model.objects.filter(
state=self.model.Running,
task_type=caller.__class__.__name__
)
if pending.exists():
raise AssertionError('Another {} task is already running'
''.format(caller.__class__.__name__))
results_id = kwargs.get(self.id_attr)
try:
result = fn(*args, **kwargs)
except Retry:
# Retry must always be raised to retry a task
raise
except Exception as e:
# Error, update stats, log/raise/return depending on values
if results_id:
self.model.update_stats(results_id, error=e)
if self.log_error:
logger.error(e)
if self.raise_error:
raise
else:
return e
else:
# No error, save results in refresh object and return
if results_id:
self.model.update_stats(results_id, **result)
return result
return wrapped
然后我们简单地run
用每种情况所需的参数装饰任务上的方法,例如:
class SomeTask(Task):
@tracked_with(RefreshResults, unique=True, log_error=False)
def run(self, *args, **kwargs)...
然后更改任务的行为(或完全删除跟踪)意味着调整一个参数,或注释掉修饰的行。超级容易实现,但更重要的是,检查时超级容易理解。