Питон:конкретный аргумент декоратора (не связанный с обернутой функцией)?
Вопрос
Я хочу создать декоратор кэширования, который с заданной функцией кэширует результат функции в местоположение, указанное в оформлении.Что- то вроде этого:
@cacheable('/path/to/cache/file')
def my_function(a, b, c):
return 'something'
Аргумент декоратора полностью отделен от аргумента функции, которую он обертывает.Я просмотрел довольно много примеров, но я не совсем понимаю, как это сделать - возможно ли иметь аргумент для декоратора, который не связан с обернутой функцией и не передается ей?
Решение
Идея заключается в том, что ваш декоратор - это функция, возвращающая декоратор.
Первый Напишите свой декоратор так, как если бы вы знали, что ваш аргумент является глобальной переменной.Давайте скажем что-то вроде:
-
def decorator(f):
def decorated(*args,**kwargs):
cache = Cache(cachepath)
if cache.iscached(*args,**kwargs):
...
else:
res = f(*args,**kwargs)
cache.store((*args,**kwargs), res)
return res
return decorated
ТОГДА Напишите функцию, которая принимает cachepath в качестве аргумента и возвращает ваш декоратор.
-
def cache(filepath)
def decorator(f):
def decorated(*args,**kwargs):
cache = Cache(cachepath)
if cache.iscached(*args,**kwargs):
...
else:
res = f(*args,**kwargs)
cache.store((*args,**kwargs), res)
return res
return decorated
return decorator
Другие советы
Да, это так.Как вы знаете, декоратор - это функция.Когда написано в форме:
def mydecorator(func):
def wrapper(*args, **kwargs):
return func(*args, **kwargs)
return wrapper
@mydecorator
def foo(a, b, c):
pass
аргумент передан в mydecorator
является ли функция foo
сама по себе.
Когда декоратор принимает аргумент, вызов @mydecorator('/path/to')
на самом деле собирается сначала вызвать функцию mydecorator с помощью '/path/to'.Затем результат вызова в mydecorator(path)
будет вызвана для получения функции foo
.Вы эффективно определяете динамическую функцию-оболочку.
В двух словах, вам нужен еще один уровень функций декоратора.
Вот этот немного глупый пример:
def addint(val):
def decorator(func):
def wrapped(*args, **kwargs):
result = func(*args, **kwargs)
return result + val
return wrapped # returns the decorated function "add_together"
return decorator # returns the definition of the decorator "addint"
# specifically built to return an extra 5 to the sum
@addint(5)
def add_together(a, b):
return a + b
print add_together(1, 2)
# prints 8, not 3
Ответ Пола хорош, я бы переместил объект кэша, чтобы его не нужно было создавать каждый раз, и спроектировал ваш кэш так, чтобы он вызывал KeyError при пропуске кэша:
def cache(filepath): def decorator(f): f._cache = Cache(cachepath) def decorated(*args,**kwargs): try: key = (args, kwargs) res = f._cache.get(key) except KeyError: res = f(*args, **kwargs) f._cache.put(key, res) return res return decorated return decorator