Питон:конкретный аргумент декоратора (не связанный с обернутой функцией)?

StackOverflow https://stackoverflow.com/questions/660727

  •  20-08-2019
  •  | 
  •  

Вопрос

Я хочу создать декоратор кэширования, который с заданной функцией кэширует результат функции в местоположение, указанное в оформлении.Что- то вроде этого:

@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
Лицензировано под: CC-BY-SA с атрибуция
Не связан с StackOverflow
scroll top