我正在尝试实现一个接受一些参数的装饰器。通常带有参数的装饰器被实现为双重嵌套闭包,如下所示:
def mydecorator(param1, param2):
# do something with params
def wrapper(fn):
def actual_decorator(actual_func_arg1, actual_func_arg2):
print("I'm decorated!")
return fn(actual_func_arg1, actual_func_arg2)
return actual_decorator
return wrapper
但我个人不喜欢这种方法,因为它非常不可读且难以理解。
所以我最终得到了这个:
class jsonschema_validate(object):
def __init__(self, schema):
self._schema = schema
def __call__(self, fn):
self._fn = fn
return self._decorator
def _decorator(self, req, resp, *args, **kwargs):
try:
jsonschema.validate(req.media, self._schema, format_checker=jsonschema.FormatChecker())
except jsonschema.ValidationError as e:
_log.exception('Validation failed: %r', e)
raise errors.HTTPBadRequest('Bad request')
return self._fn(req, resp, *args, **kwargs)
这个想法非常简单:在实例化时,我们只捕获装饰器参数,在调用时,我们捕获装饰器函数并返回装饰器实例的绑定方法。绑定它很重要,因为在装饰器调用时我们想要访问self
所有信息都存储在其中。
然后我们在某个类上使用它:
class MyResource(object):
@jsonschema_validate(my_resource_schema)
def on_post(self, req, resp):
pass
不幸的是,这种方法行不通。问题是,在装饰器调用时,我们会丢失装饰实例的上下文,因为在装饰时(定义类时)装饰方法未绑定。绑定稍后在属性访问时发生。但此时我们已经有了装饰器的绑定方法(jsonschema_validate._decorator
) and self
是隐式传递的,它的值isn't MyResource
实例,而是jsonschema_validate
实例。和我们不想失去 this self
value 因为我们想在装饰器调用时访问它的属性。最终结果是TypeError
打电话时self._fn(req, resp, *args, **kwargs)
抱怨“缺少必需的位置参数‘resp’”,因为传入req
arg 变为MyResource.on_post
"self
”并且所有论点都有效地“转移”。
那么,有没有一种方法可以将装饰器实现为一个类而不是一堆嵌套函数呢?
Note
由于我第一次尝试将装饰器实现为简单类很快就失败了,因此我立即恢复到嵌套函数。似乎正确实现的类方法更加难以阅读和纠结,但无论如何我都想找到解决方案以获取乐趣。
UPDATE
终于找到解决办法了,看我自己的答案。