Как передать параметры в параметризованный декоратор?

День добрый.

Как передаете ЛЮБОЕ кол-во параметров в функцию через параметризованный декоратор? Вот функция

def parametrized_decoder(path_to_log = "files/log.txt"):
  def decorator_logo(fun):
    def wrapper(*args, **kwargs):
      import pathlib
      import datetime
      log = {}
      fmt = "%Y-%m-%d %H:%M:%S"
      time_start = datetime.datetime.now().strftime(fmt)
      name_fun = fun.__name__
      pesponse = tuple(fun(*args, **kwargs))
       
        ...
      return pesponse

    return wrapper
  return decorator_logo

Декорируемая

@parametrized_decoder
def yandex_request_put_foldr(path_in_root, folder_of_putting, header):
  ...
  return

В данной версии ругается - мол подаете 1 параметр , а требуется 3

TypeError: decorator_logo() takes 1 positional argument but 3 were given

Задача - декоратор вешать на любую функцию. Но где-то есть внешние данные, гдк-то нету. Как в этом случает параметризовать декоратор...


Ответы (2 шт):

Автор решения: Tryd0g0lik

Вот хорошая статья

Вопрос закрыт, ответ найден.

→ Ссылка
Автор решения: insolor

Если отвечать буквально на вопрос в заголовке — просто в скобках после имени декоратора указываете параметр, как для функции (параметризованный декоратор — это и есть функция, которая возвращает декоратор):

@parametrized_decoder("files/some_other_log.txt")
def yandex_request_put_foldr(path_in_root, folder_of_putting, header):
  ...
  return

Это примерно аналогично такому коду:

decorator = parametrized_decoder("files/some_other_log.txt")

@decorator
def yandex_request_put_foldr(path_in_root, folder_of_putting, header):
  ...
  return

Т.е. вызывается параметризованный декоратор с нужным параметром, он возвращает непараметризованный декоратор, и этот декоратор уже декорирует функцию.

Конкретно ваш декоратор имеет ровно параметр со значением по умолчанию, поэтому можно добавлять его без параметров, но скобки все равно должны остаться.


Параметризованный декоратор нужно добавлять со скобками, даже если явно параметры никакие не передаются.

Если добавить такой декоратор без скобок, то во внешнюю функцию параметром будет передана декорируемая функция (как если бы это был обычный декоратор, а не параметризованный), а функция decorator_logo будет использована как wrapper, и в нее будут переданы все три параметра декорируемой функции, а decorator_logo может принять только один, из-за этого ошибка:

TypeError: parametrized_decoder.<locals>.decorator_logo() takes 1 positional argument but 3 were given

Со скобками все будет работать:

def parametrized_decoder(path_to_log="files/log.txt"):
    def decorator_logo(fun):
        def wrapper(*args, **kwargs):
            print("Hello from decorator!")
            print(f"Path to log: {path_to_log}")
            response = fun(*args, **kwargs)
            return response

        return wrapper

    return decorator_logo


@parametrized_decoder()  # <-- добавлены скобки
def yandex_request_put_foldr(path_in_root, folder_of_putting, header):
    print("Hello from function!")
    return


yandex_request_put_foldr("sdfsdf", "wewew", "erhgerh")

Вывод:

Hello from decorator!
Path to log: files/log.txt
Hello from function!

Чтобы параметризованный декоратор мог работать без скобок, нужно добавить явную проверку, что если первый параметр - функция, то вызвать decortator_logo с этой функцией и возвращать результат из parametrized_decoder:

from typing import Callable


def parametrized_decoder(path_to_log=None):
    path_to_log_default = "files/log.txt"
    path_to_log = path_to_log or path_to_log_default

    def decorator_logo(fun):
        def wrapper(*args, **kwargs):
            print("Hello from decorator!")
            print(f"Path to log: {path_to_log}")
            response = fun(*args, **kwargs)
            return response

        return wrapper

    if isinstance(path_to_log, Callable):
        func = path_to_log
        path_to_log = path_to_log_default
        return decorator_logo(func)

    return decorator_logo


@parametrized_decoder
def yandex_request_put_foldr(path_in_root, folder_of_putting, header):
    print("Hello from function!")
    return


yandex_request_put_foldr("sdfsdf", "wewew", "erhgerh")
→ Ссылка