Python装饰器函数:如何用装饰器增强函数功能
Python装饰器是一种用于在不修改原函数源代码的情况下增强函数功能的方法。装饰器函数可以通过在被装饰的函数前后执行额外的代码来扩展其功能。本文将介绍装饰器的基本语法和示例,并具体说明不同类型的装饰器的用途及实现方法。
一、基本语法
装饰器的基本语法如下:
def decorator(func):
def wrapper(*args, **kwargs):
# 在被装饰函数之前执行的代码
result = func(*args, **kwargs)
# 在被装饰函数之后执行的代码
return result
return wrapper
@decorator
def function():
# 原函数的代码
以上是一个装饰器的基本结构。装饰器函数接受一个函数作为参数,并返回一个新的函数。新的函数通常称为"包装器",用于替代原有的函数。在包装器中,可以在调用原函数前后加入一些额外的代码,以增强函数的功能。
二、装饰器的功能
装饰器可以用于实现以下功能:
1. 日志记录
装饰器可以用来记录函数的调用信息,如函数名、参数、返回值等,以便进行调试和性能分析。下面是一个示例:
import logging
def log(func):
def wrapper(*args, **kwargs):
logging.info("正在调用函数 %s" % func.__name__)
result = func(*args, **kwargs)
logging.info("函数 %s 的返回值为 %s" % (func.__name__, result))
return result
return wrapper
@log
def add(a, b):
return a + b
在上述示例中,装饰器函数log接受一个函数作为参数,并返回一个新的函数wrapper。wrapper函数首先记录函数的调用信息,然后调用原有的函数并返回结果。通过在add函数前使用@log装饰器,可以自动记录函数的调用信息。
2. 参数验证
装饰器可以用来验证函数的输入参数是否符合要求。下面是一个示例:
def check_args(func):
def wrapper(*args, **kwargs):
for arg in args:
if not isinstance(arg, int):
raise TypeError("参数必须为整数")
for key, value in kwargs.items():
if not isinstance(value, int):
raise TypeError("参数必须为整数")
return func(*args, **kwargs)
return wrapper
@check_args
def add(a, b=0):
return a + b
在上述示例中,装饰器函数check_args接受一个函数作为参数,并返回一个新的函数wrapper。wrapper函数首先验证输入参数是否为整数,如果不符合要求则抛出异常;否则调用原有的函数并返回结果。通过在add函数前使用@check_args装饰器,可以自动验证函数的输入参数。
3. 缓存结果
装饰器可以用来缓存函数的计算结果,以减少重复计算的开销。下面是一个示例:
def cache_result(func):
cache = {}
def wrapper(*args, **kwargs):
key = str(args) + str(kwargs)
if key in cache:
return cache[key]
result = func(*args, **kwargs)
cache[key] = result
return result
return wrapper
@cache_result
def fibonacci(n):
if n <= 1:
return n
return fibonacci(n - 1) + fibonacci(n - 2)
在上述示例中,装饰器函数cache_result接受一个函数作为参数,并返回一个新的函数wrapper。cache_result函数内部定义了一个cache字典,用于存储函数的计算结果。wrapper函数首先计算函数的参数的哈希值作为cache的键,如果该键已经存在于cache中,则直接返回结果;否则调用原有的函数计算结果,并存入cache中。通过在fibonacci函数前使用@cache_result装饰器,可以自动缓存函数的计算结果。
三、常见的装饰器类型
根据应用场景的不同,可以实现不同类型的装饰器。
1. 无参装饰器
无参装饰器是最简单的装饰器类型,它对被装饰的函数不做任何修改。下面是一个示例:
def decorator(func):
def wrapper(*args, **kwargs):
result = func(*args, **kwargs)
return result
return wrapper
@decorator
def function():
# 原函数的代码
在上述示例中,装饰器函数decorator没有参数,直接返回原函数。使用@decorator装饰器时,原函数function的行为与没有装饰器时完全一致。
2. 带参数的装饰器
带参数的装饰器可以接受额外的参数,并根据这些参数的不同修改被装饰的函数的行为。下面是一个示例:
def add_suffix(suffix):
def decorator(func):
def wrapper(*args, **kwargs):
result = func(*args, **kwargs)
return result + suffix
return wrapper
return decorator
@add_suffix("!")
def welcome(name):
return "Hello, " + name
@add_suffix("!!!")
def shout(message):
return message.upper()
在上述示例中,装饰器函数add_suffix接受一个参数suffix,并返回一个装饰器函数decorator。decorator函数接受一个函数作为参数,并返回一个新的函数wrapper。wrapper函数在调用原函数后,还将结果与suffix拼接在一起。通过在welcome函数和shout函数前使用@add_suffix("...")装饰器,可以在函数的结果后添加额外的后缀。
3. 类装饰器
类装饰器是一种特殊类型的装饰器,它使用一个类来装饰函数。这种装饰器的主要用途是实现状态的保持。下面是一个示例:
class Timer:
def __init__(self, func):
self.func = func
def __call__(self, *args, **kwargs):
start_time = time.time()
result = self.func(*args, **kwargs)
end_time = time.time()
print("函数 %s 的执行时间为 %.2f 秒" % (self.func.__name__, end_time - start_time))
return result
@Timer
def calculate_sum(n):
return sum(range(n))
在上述示例中,类Timer接受一个函数作为参数,并在__init__方法中将该函数保存为实例变量self.func。__call__方法定义了类实例调用时的行为,其中包括计算函数执行时间并输出结果。通过在calculate_sum函数前使用@Timer装饰器,可以自动计算函数的执行时间。
四、总结
通过使用装饰器,我们可以在不修改函数源代码的情况下增强函数的功能。装饰器的基本语法是定义一个装饰器函数,该函数接受一个函数作为参数,并返回一个新的函数。装饰器可以用于实现日志记录、参数验证、结果缓存等功能。常见的装饰器类型包括无参装饰器、带参数的装饰器和类装饰器。掌握装饰器的使用方法,可以更加灵活地扩展函数的功能,提高代码的可读性和复
