Aut*_*hre 0 python function decorator
有谁知道在 Python 中调用函数时是否可以将参数传递给装饰器?
到目前为止,我只在函数定义中看到:
@decorator("This is a decorator", 66)
def func(a: int, b: int) -> None:
pass
Run Code Online (Sandbox Code Playgroud)
但我想知道是否可以在调用函数时执行此操作。
对于那些想知道为什么我要这样做的人来说,这是因为我正在继续其他人的工作,他在代码中大量使用了装饰器。但到目前为止,他只需要在函数定义时传递参数,因为该函数只被调用一次。装饰器用于打印有关该函数所使用的上下文的信息日志,但在我的情况下,由于可以在不同的位置调用该函数,因此调用该函数的函数的上下文可能不同。
正如其他人在这里所写的,装饰器是一个函数的语法糖(即,使程序更易于阅读、编写或理解),该函数接收另一个函数作为参数并从内部激活它。
\n因此,使用装饰器调用 \xe2\x80\x9cAdd()\xe2\x80\x9d 函数,如下所示:
\n@wrapper()\ndef Add(x: int, y: int):\n return x + y\n
Run Code Online (Sandbox Code Playgroud)\n这就像使用 \xe2\x80\x9cAdd\xe2\x80\x9d 函数作为变量来调用 \xe2\x80\x9cwrapper\xe2\x80\x9d 函数一样。像这样:
\n wrapper(Add)(x,y) # pass x,y to wrapper that pass it to Add function.\n|wrapper|\n|----Add----|()\n
Run Code Online (Sandbox Code Playgroud)\n(我认为)向装饰器添加参数的最佳方法是将其全部嵌套在另一个包含子装饰器的函数下。例如:
\n@deco_maker(msg: str)\ndef Add(x: int, y: int):\n return x + y\n
Run Code Online (Sandbox Code Playgroud)\n将是这样的:
\n deco_maker(msg)(Add)(x,y)\n|--wrapper-|\n|-wrapper_func-|\n|---------Add-------|\n
Run Code Online (Sandbox Code Playgroud)\n这是一个简单的包装装饰器,它记录不带参数的函数调用,如下所示:
\ndef wrapper(func: Callable):\n def wrapper_func(*args, **kwargs):\n logging.DEBUG f"Function \'{func.__name__}\' called with args: {[str(arg) for arg in args]}."\n value = func(*args, **kwargs)\n return value\n return wrapper_func\n
Run Code Online (Sandbox Code Playgroud)\n这是带有相关日志记录参数的扩展装饰器(日志名称和级别以获得更大的灵活性):
\ndef log_func_calls(logger_name: str, log_level: int):\n def wrapper(func: Callable):\n def wrapper_func(*args, **kwargs):\n logger = logging.getLogger(logger_name)\n logger.log(\n level=log_level,\n msg=f"Function \'{func.__name__}\' called with args: {[str(arg) for arg in args]}."\n )\n value = func(*args, **kwargs)\n return value\n return wrapper_func\n return wrapper\n
Run Code Online (Sandbox Code Playgroud)\n以下是用于记录函数调用的参数化装饰器的完整代码示例,以及在其后打印的日志文件输出。
\n例子:
\nimport logging\nfrom typing import Callable\n\n# define app logger with file and console handlers\ndef setup_logging():\n logger = logging.getLogger(\'test_app\')\n logger.setLevel(logging.DEBUG)\n # create file handler which logs even debug messages\n fh = logging.FileHandler(\'test.log\')\n fh.setLevel(logging.DEBUG)\n # create formatter and add it to the file handler\n formatter = logging.Formatter(\'{asctime} | {name} | {levelname:^8s} | {message}\', style=\'{\')\n fh.setFormatter(formatter)\n # add the handler to the logger\n logger.addHandler(fh)\n return logger\n\n# define a log decorator to trace function calls\ndef log_func_calls(logger_name: str, log_level: int):\n def wrapper(func: Callable):\n def wrapper_func(*args, **kwargs):\n logger = logging.getLogger(logger_name)\n logger.log(\n level=log_level,\n msg=f"Function \'{func.__name__}\' called with args: {[str(arg) for arg in args]}."\n )\n value = func(*args, **kwargs)\n return value\n return wrapper_func\n return wrapper\n\n# sample usage 1\n@log_func_calls(logger_name=\'test_app\', log_level=logging.DEBUG)\ndef Add(x: int, y: int):\n return x + y\n\n# sample usage 2\n@log_func_calls(logger_name=\'test_app\', log_level=logging.DEBUG)\ndef Sub(x: int, y: int):\n return x - y\n\n# a test run\ndef main():\n logger = setup_logging()\n logger.info("<<< App started ! >>>")\n print(Add(50,7))\n print(Sub(10,7))\n print(Add(50,70))\n logger.info("<<< App Ended ! >>>")\n\nif __name__ == "__main__":\n main()\n
Run Code Online (Sandbox Code Playgroud)\n以及日志输出:
\n...\n2022-06-19 23:34:52,656 | test_app | DEBUG | Function \'Add\' called with args: [\'50\', \'7\'].\n2022-06-19 23:34:52,656 | test_app | DEBUG | Function \'Sub\' called with args: [\'10\', \'7\'].\n2022-06-19 23:34:52,657 | test_app | DEBUG | Function \'Add\' called with args: [\'50\', \'70\'].\n...\n
Run Code Online (Sandbox Code Playgroud)\n