有谁知道在 Python 中调用函数时是否可以将参数传递给装饰器?
到目前为止,我只在函数定义上看到过这种东西:
@decorator("This is a decorator", 66)
def func(a: int, b: int) -> None:
pass
但我想知道是否可以在调用函数时做到这一点。
类似这样的:
func(a, b)(66) # 66 is passed by argument to @decorator
对于那些想知道我为什么要这样做的人来说,这是因为我正在继续其他人的工作,他在代码中大量使用了装饰器。但到目前为止,他只需要在函数定义时传递参数,因为该函数只被调用一次。装饰器用于打印有关该函数使用的上下文的信息日志,但在我的例子中,由于可以在不同的位置调用该函数,因此调用该函数的函数的上下文可能不同。
正如其他人在这里所写的,装饰器是一个函数的语法糖(即,使程序更易于阅读、编写或理解),该函数接收另一个函数作为参数并从内部激活它。
因此,用装饰器调用这个“Add()”函数,如下所示:
@wrapper()
def Add(x: int, y: int):
return x + y
这就像用“Add”函数作为变量来调用“wrapper”函数一样。像这样:
wrapper(Add)(x,y) # pass x,y to wrapper that pass it to Add function.
|wrapper|
|----Add----|()
(我认为)向装饰器添加参数的最佳方法是将其全部嵌套在另一个包含子装饰器的函数下。例如:
@deco_maker(msg: str)
def Add(x: int, y: int):
return x + y
将是这样的:
deco_maker(msg)(Add)(x,y)
|--wrapper-|
|-wrapper_func-|
|---------Add-------|
这是一个简单的包装装饰器,它记录不带参数的函数调用,如下所示:
def wrapper(func: Callable):
def wrapper_func(*args, **kwargs):
logging.DEBUG f"Function '{func.__name__}' called with args: {[str(arg) for arg in args]}."
value = func(*args, **kwargs)
return value
return wrapper_func
这是具有相关日志记录参数的扩展装饰器(日志名称和级别以获得更大的灵活性):
def log_func_calls(logger_name: str, log_level: int):
def wrapper(func: Callable):
def wrapper_func(*args, **kwargs):
logger = logging.getLogger(logger_name)
logger.log(
level=log_level,
msg=f"Function '{func.__name__}' called with args: {[str(arg) for arg in args]}."
)
value = func(*args, **kwargs)
return value
return wrapper_func
return wrapper
这里是用于记录函数调用的参数化装饰器的完整代码示例,以及在其后打印的日志文件输出。
示例:
import logging
from typing import Callable
# define app logger with file and console handlers
def setup_logging():
logger = logging.getLogger('test_app')
logger.setLevel(logging.DEBUG)
# create file handler which logs even debug messages
fh = logging.FileHandler('test.log')
fh.setLevel(logging.DEBUG)
# create formatter and add it to the file handler
formatter = logging.Formatter('{asctime} | {name} | {levelname:^8s} | {message}', style='{')
fh.setFormatter(formatter)
# add the handler to the logger
logger.addHandler(fh)
return logger
# define a log decorator to trace function calls
def log_func_calls(logger_name: str, log_level: int):
def wrapper(func: Callable):
def wrapper_func(*args, **kwargs):
logger = logging.getLogger(logger_name)
logger.log(
level=log_level,
msg=f"Function '{func.__name__}' called with args: {[str(arg) for arg in args]}."
)
value = func(*args, **kwargs)
return value
return wrapper_func
return wrapper
# sample usage 1
@log_func_calls(logger_name='test_app', log_level=logging.DEBUG)
def Add(x: int, y: int):
return x + y
# sample usage 2
@log_func_calls(logger_name='test_app', log_level=logging.DEBUG)
def Sub(x: int, y: int):
return x - y
# a test run
def main():
logger = setup_logging()
logger.info("<<< App started ! >>>")
print(Add(50,7))
print(Sub(10,7))
print(Add(50,70))
logger.info("<<< App Ended ! >>>")
if __name__ == "__main__":
main()
以及日志输出:
...
2022-06-19 23:34:52,656 | test_app | DEBUG | Function 'Add' called with args: ['50', '7'].
2022-06-19 23:34:52,656 | test_app | DEBUG | Function 'Sub' called with args: ['10', '7'].
2022-06-19 23:34:52,657 | test_app | DEBUG | Function 'Add' called with args: ['50', '70'].
...
简短的回答是否,但您可以修改装饰器函数的代码;例如:
def decorator(func):
def inner(*args,**kwargs):
return func(*args,**kwargs)
return inner
这是一个标准的装饰器函数,让我们稍微改变一下。
你可以这样做:
def decorator(func,defaultdescription,defaultnum):
def inner(description=defaultdescription,num=defaultnum,*args,**kwargs):
print("function",func.__code__.co_name,"with description",description,"with number",num)
return func(*args,**kwargs)
return inner
这样您就可以更改功能的描述
基本上,装饰器是另一个内部的函数,它在完成后调用您的函数。如果您打算使用自己的装饰器,则必须有一个内部函数来执行所需的任务并调用使用装饰器函数的函数。你可以使用 *args 和 **kwargs
下面的代码是Python装饰器的基本实现
# Python code to illustrate
# Decorators basic in Python
def decorator_fun(func):
def inner(*args, **kwargs):
"""
do your actual task here
"""
for argv in args:
print(argv)
for key in kwargs:
print(key)
#returning to function which used the decorator at last
func()
return inner
@decorator_fun
def func_to():
print("Inside actual function")
func_to("hello",{"test": "value 1"})