Я пытаюсь реализовать декоратор, который принимает некоторые аргументы.Обычно декораторы с аргументами реализованы в виде двойных вложений, например:
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
), и self
передается неявно, и его значение не является MyResource
экземпляром, скорее jsonschema_validate
экземпляром.И мы не хотим потерять это self
значение, потому что мы хотим получить доступ к его атрибутам во время вызова декоратора.В итоге это приводит к TypeError
при вызове self._fn(req, resp, *args, **kwargs)
с жалобами на то, что «требуемый позиционный аргумент 'resp' отсутствует", потому что переданный в req
arg становится MyResource.on_post
"self
" и все аргументы фактически "сдвигаются".
Итак, есть ли способ реализовать декоратор как класс, а не как набор вложенных функций?
Примечание
Как моя первая попытка реализовать декоратор как простой классдовольно быстро вышел из строя, я сразу же вернулся к вложенным функциям.Кажется, что правильно реализованный подход к классу еще более нечитабелен и запутан, но я все равно хочу найти решение для удовольствия.