Как передать декоратору параметры из конфига
Такая задача. Программа считывает параметры работы из терминала, при помощи argparse. Попадают они туда через json файлы конфигов, притом не в том виде, в котором их привык видеть argparse, но не суть, я их привожу к нужному ему виду и паршу уже список строк в нужном ему виде, условно
parser = argparse.ArgumentParser
args = transform_input_args(sys.argv[1:])
final_args = parser.parse_argument(args)
Но вопрос не в этом. У меня есть самописный декоратор с параметрами. Мне бы очень хотелось, чтобы эти параметры задавались в файле конфига. Основная логика скрипта находится в блоке if __name__ == '__main__', на некоторые вызываемые из него функции этот декоратор и повешен. Получается, считывание аргументов идет в нем, но как я понял у декоратора параметры должны быть установлены ДО Run time и потому у меня не выходит установить у него считанные из конфига параметры.
Кто нибудь в курсе как можно провернуть мою задумку?
Ответы (1 шт):
Если вам нужны параметры на момент вызова декорированной функции (а не в момент привязки декоратора), то можно создать объект с методом-декоратором. Сначала объект создается, его метод навешивается на функции. Потом в блоке if __name__ == "__main__": вы устанавливаете нужные параметры в объект, потом вызываете функции.
Пример:
import functools
class RunLogger:
file_name: str = None
def decorator(self, func):
@functools.wraps(func)
def wrapper(*args, **kwargs):
if self.file_name is not None:
with open(self.file_name, "a") as file:
print(f"Функция {func.__name__} вызвана с параметрами args={args}, kwargs={kwargs}", file=file)
else:
print("Имя файля для декоратора не установлено")
return func(*args, **kwargs)
return wrapper
logger = RunLogger()
@logger.decorator
def plus(a, b):
return a + b
if __name__ == "__main__":
logger.file_name = "calls.txt"
print(plus(2, b=3))
Выведет 5, в файл calls.txt запишется Функция plus вызвана с параметрами args=(2,), kwargs={'b': 3}.
Также можно сам декоратор реализовать не как функцию, а как класс с методом __call__, в объект этого класса аналогично перед вызовом декорируемой функции можно прописать нужные параметры. Все аналогично, только имя метода decorator в примере выше меняется на __call__, и функция декорируется самим объектом, а не его методом:
import functools
class RunLogger:
file_name: str = None
def __call__(self, func):
@functools.wraps(func)
def wrapper(*args, **kwargs):
if self.file_name is not None:
with open(self.file_name, "a") as file:
print(f"Функция {func.__name__} вызвана с параметрами args={args}, kwargs={kwargs}", file=file)
else:
print("Имя файля для декоратора не установлено")
return func(*args, **kwargs)
return wrapper
logger = RunLogger()
@logger
def plus(a, b):
return a + b
if __name__ == "__main__":
logger.file_name = "calls.txt"
print(plus(2, b=3))
Еще вариант - использовать обычный параметризованный декоратор, при инициализации передавать в него какой-то объект контейнер параметров, потом до вызова функции записывать в контейнер конкретные значения:
import functools
class Config:
file_name: str = None
def parametrized_decorator(config: Config):
def decorator(func):
@functools.wraps(func)
def wrapper(*args, **kwargs):
if config.file_name is not None:
with open(config.file_name, "a") as file:
print(f"Функция {func.__name__} вызвана с параметрами args={args}, kwargs={kwargs}", file=file)
else:
print("Имя файля для декоратора не установлено")
return func(*args, **kwargs)
return wrapper
return decorator
config = Config()
@parametrized_decorator(config)
def plus(a, b):
return a + b
if __name__ == "__main__":
config.file_name = "calls.txt"
print(plus(2, b=3))