Python 如何在装饰器中使用其他函数?

在装饰器中使用其他函数是一种常见的需求,可以帮助你将复杂的功能分解成更小、更易于管理的部分。
首页 新闻资讯 行业资讯 Python 如何在装饰器中使用其他函数?

前言

79f6173256233ebfb2037348c9c9f9f08e221b.jpg

在装饰器中使用其他函数是一种常见的需求,可以帮助你将复杂的功能分解成更小、更易于管理的部分。

使用辅助函数进行日志记录

假设你有一个装饰器,用于在函数调用前后记录日志。你可以将日志记录的功能提取到一个单独的辅助函数中。

importlogging
# 配置日志记录
logging.basicConfig(level=logging.INFO)deflog_message(message):logging.info(message)deflog_function_call(func):defwrapper(*args,**kwargs):log_message(f"Calling {func.__name__} with args={args} kwargs={kwargs}")result=func(*args,**kwargs)log_message(f"{func.__name__} returned {result}")returnresultreturnwrapper
@log_function_call
defadd(a,b):returna+badd(3,5)

使用辅助函数进行输入验证

假设你有一个装饰器,用于验证函数的输入参数。你可以将输入验证的逻辑提取到一个单独的辅助函数中。

defvalidate_input(*types):defdecorator(func):defwrapper(*args,**kwargs):iflen(args)!=len(types):raiseTypeError("Number of arguments does not match expected types")forarg,type_inzip(args,types):ifnotisinstance(arg,type_):raiseTypeError(f"Argument {arg} is not of type {type_}")returnfunc(*args,**kwargs)returnwrapperreturndecorator
defis_positive(number):ifnumber<=0:raiseValueError("Number must be positive")returnnumber
@validate_input(int,int)defadd(a,b):returna+b
@validate_input(int)defsquare(n):returnn**2add(3,5)# 正常运行square(4)# 正常运行
#square(-4)# 抛出 ValueError

使用辅助函数进行缓存

假设你有一个装饰器,用于缓存函数的结果。你可以将缓存的逻辑提取到一个单独的辅助函数中。

from functoolsimportlru_cache
defcache_results(func):@lru_cache(maxsize=128)defwrapper(*args,**kwargs):returnfunc(*args,**kwargs)returnwrapper
defcompute_fibonacci(n):ifn<=1:returnnreturncompute_fibonacci(n-1)+compute_fibonacci(n-2)@cache_results
deffibonacci(n):returncompute_fibonacci(n)print(fibonacci(10))# 输出:55print(fibonacci(10))# 直接从缓存中获取结果

使用辅助函数进行权限验证

假设你有一个装饰器,用于验证用户是否有权限调用某个函数。你可以将权限验证的逻辑提取到一个单独的辅助函数中。

defcheck_permission(permission):defdecorator(func):defwrapper(*args,**kwargs):ifnothas_permission(permission):raisePermissionError(f"User does not have permission {permission}")returnfunc(*args,**kwargs)returnwrapperreturndecorator
defhas_permission(permission):# 假设这里有一个权限检查的逻辑returnpermission=="admin"@check_permission("admin")defadmin_action():print("Performing admin action")@check_permission("user")defuser_action():print("Performing user action")admin_action()# 正常运行
#user_action()# 抛出 PermissionError

使用辅助函数进行性能测量

假设你有一个装饰器,用于测量函数的执行时间。你可以将性能测量的逻辑提取到一个单独的辅助函数中。

importtime
defmeasure_time(func):defwrapper(*args,**kwargs):start_time=time.time()result=func(*args,**kwargs)end_time=time.time()print(f"{func.__name__} took {end_time - start_time:.4f} seconds to execute")returnresultreturnwrapper
defget_current_time():returntime.time()@measure_time
defslow_function():time.sleep(2)print("Slow function completed")slow_function()

如何在装饰器中使用多个装饰器?

基本的多重装饰器

假设你有两个装饰器 @log_function_call 和 @measure_time,分别用于日志记录和性能测量。

importloggingimporttime
# 配置日志记录
logging.basicConfig(level=logging.INFO)deflog_message(message):logging.info(message)deflog_function_call(func):defwrapper(*args,**kwargs):log_message(f"Calling {func.__name__} with args={args} kwargs={kwargs}")result=func(*args,**kwargs)log_message(f"{func.__name__} returned {result}")returnresultreturnwrapper
defmeasure_time(func):defwrapper(*args,**kwargs):start_time=time.time()result=func(*args,**kwargs)end_time=time.time()print(f"{func.__name__} took {end_time - start_time:.4f} seconds to execute")returnresultreturnwrapper
@log_function_call
@measure_time
defadd(a,b):time.sleep(1)# 模拟耗时操作returna+b
result=add(3,5)print(f"Result: {result}")

带参数的多重装饰器

假设你有一个带参数的装饰器 @repeat,用于多次调用函数,同时还有一个 @log_function_call 装饰器。

importlogging
# 配置日志记录
logging.basicConfig(level=logging.INFO)deflog_message(message):logging.info(message)deflog_function_call(func):defwrapper(*args,**kwargs):log_message(f"Calling {func.__name__} with args={args} kwargs={kwargs}")result=func(*args,**kwargs)log_message(f"{func.__name__} returned {result}")returnresultreturnwrapper
defrepeat(num_times):defdecorator(func):defwrapper(*args,**kwargs):results=[]for_inrange(num_times):result=func(*args,**kwargs)results.append(result)returnresultsreturnwrapperreturndecorator
@log_function_call
@repeat(3)defgreet(name):returnf"Hello, {name}!"results=greet("Alice")forresultinresults:print(result)

使用 functools.wraps 保留元数据

为了保留被装饰函数的元数据(如名称、文档字符串等),可以使用 functools.wraps。

importloggingimporttimeimportfunctools
# 配置日志记录
logging.basicConfig(level=logging.INFO)deflog_message(message):logging.info(message)deflog_function_call(func):@functools.wraps(func)defwrapper(*args,**kwargs):log_message(f"Calling {func.__name__} with args={args} kwargs={kwargs}")result=func(*args,**kwargs)log_message(f"{func.__name__} returned {result}")returnresultreturnwrapper
defmeasure_time(func):@functools.wraps(func)defwrapper(*args,**kwargs):start_time=time.time()result=func(*args,**kwargs)end_time=time.time()print(f"{func.__name__} took {end_time - start_time:.4f} seconds to execute")returnresultreturnwrapper
@log_function_call
@measure_time
defadd(a,b):time.sleep(1)# 模拟耗时操作returna+b
result=add(3,5)print(f"Result: {result}")print(add.__name__)# 输出:addprint(add.__doc__)# 输出:None 或者函数的文档字符串

组合多个带参数的装饰器

假设你有两个带参数的装饰器 @repeat 和 @log_level,分别用于多次调用函数和设置日志级别。

importloggingimportfunctools
# 配置日志记录
logging.basicConfig(level=logging.DEBUG)defset_log_level(level):defdecorator(func):@functools.wraps(func)defwrapper(*args,**kwargs):logger=logging.getLogger(func.__name__)logger.setLevel(level)result=func(*args,**kwargs)returnresultreturnwrapperreturndecorator
defrepeat(num_times):defdecorator(func):@functools.wraps(func)defwrapper(*args,**kwargs):results=[]for_inrange(num_times):result=func(*args,**kwargs)results.append(result)returnresultsreturnwrapperreturndecorator
@set_log_level(logging.INFO)@repeat(3)defgreet(name):logger=logging.getLogger(greet.__name__)logger.info(f"Greeting {name}")returnf"Hello, {name}!"results=greet("Alice")forresultinresults:print(result)

总结

通过将装饰器中的复杂逻辑提取到单独的辅助函数中,可以使装饰器更加模块化和易于维护。这些辅助函数可以被多个装饰器复用,从而提高代码的重用性和可读性。

通过组合多个装饰器,可以实现更复杂的功能。多个装饰器的执行顺序是从内到外,因此最靠近函数定义的装饰器会首先被应用。使用 functools.wraps 可以保留被装饰函数的元数据,使代码更加清晰和易读。希望这些示例能帮助你更好地理解如何在装饰器中使用多个装饰器。

23    2024-11-04 15:30:43    Python 装饰器 函数