Python中的装饰器函数完全指南
Python中的装饰器函数是一个非常强大的功能,可以让我们在不改变函数本身的情况下,增加一些额外的功能。本文将全面介绍Python中的装饰器,包括其定义、用法、实现和常见的应用场景等。
1. 装饰器函数的定义
在Python中,装饰器函数是一个将函数作为参数并返回一个新函数的函数。它可以用来修改、增强或包装原始函数的功能。
装饰器函数的基本语法为:
def decorator_function(original_function):
def new_function(*args, **kwargs):
# 在新函数中增加额外的功能
return original_function(*args, **kwargs)
return new_function
其中,original_function是需要被增强功能的原始函数,new_function是一个新的函数,它会包装原始函数并增加一些额外的功能。
2. 装饰器函数的用法
使用装饰器函数可以给函数增加各种各样的功能,例如:
- 记录函数的运行时间;
- 给函数增加参数验证;
- 缓存函数的计算结果。
我们可以通过装饰器函数来实现这些功能,而不需要修改原始函数的代码。例如:
import time
def timer(original_function):
def new_function(*args, **kwargs):
start_time = time.time()
result = original_function(*args, **kwargs)
end_time = time.time()
print(f"{original_function.__name__}运行时间:{end_time - start_time:.2f}s")
return result
return new_function
@timer
def my_function():
time.sleep(2)
my_function()
上面的代码中,timer装饰器函数会记录被装饰函数的运行时间,并在函数运行结束后输出运行时间。使用@timer语法糖,就可以在不改变my_function函数的代码的情况下,为它增加一个计时器的功能。
3. 装饰器函数的实现
在Python中,装饰器函数本质上就是一个普通的Python函数。它可以使用所有Python函数支持的语法和特性,例如:
- 指定函数参数和返回值类型;
- 使用闭包存储某些状态等。
我们可以使用装饰器函数实现各种各样的装饰器。
3.1 同时使用多个装饰器
在Python中,可以同时使用多个装饰器来增强函数的功能。例如:
import time
def timer(original_function):
def new_function(*args, **kwargs):
start_time = time.time()
result = original_function(*args, **kwargs)
end_time = time.time()
print(f"{original_function.__name__}运行时间:{end_time - start_time:.2f}s")
return result
return new_function
def logger(original_function):
def new_function(*args, **kwargs):
print(f"函数{original_function.__name__}被调用了,参数为:args={args}, kwargs={kwargs}")
result = original_function(*args, **kwargs)
print(f"函数{original_function.__name__}的执行结果为:{result}")
return result
return new_function
@logger
@timer
def my_function():
time.sleep(2)
my_function()
上面的代码中,timer装饰器函数会记录被装饰函数的运行时间,logger装饰器函数会记录被装饰函数的调用参数和执行结果。使用@logger和@timer语法糖,就可以在不改变my_function函数的代码的情况下,为它增加两个装饰器的功能。
需要注意的是,使用多个装饰器时,装饰器的执行顺序是从下往上的,也就是先执行最后一个装饰器,然后依次向上执行。
3.2 使用类实现装饰器
在Python中,装饰器函数也可以使用类来实现,这通常情况下比使用函数实现更加灵活。例如:
import time
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(f"{self.func.__name__}运行时间:{end_time - start_time:.2f}s")
return result
@Timer
def my_function():
time.sleep(2)
my_function()
上面的代码中,Timer类实现了__init__和__call__两个方法,前者用来初始化实例,后者用来包装原始函数和增强它的功能。使用@Timer语法糖,就可以为my_function函数增加一个计时器的功能。
需要注意的是,使用类实现装饰器时,需要将类实例化后才能使用,例如:
timer = Timer(my_function) timer()
4. 常见的装饰器应用场景
装饰器函数是Python中的一个非常强大的功能,它可以用于实现各种各样的功能。以下是一些常见的装饰器应用场景:
4.1 参数验证
在Python中,我们经常需要对函数的输入参数进行验证,以防止程序出错。使用装饰器可以很方便地实现这个功能。例如:
def check_input(original_function):
def new_function(*args, **kwargs):
for arg in args:
if not isinstance(arg, int):
raise ValueError("参数必须为整数!")
return original_function(*args, **kwargs)
return new_function
@check_input
def my_function(x, y):
print(x + y)
my_function(1, 2) # 正常调用,输出3
my_function("a", 2) # 抛出异常
上面的代码中,check_input装饰器函数会检查my_function函数的输入参数是否为整数,如果不是,则抛出异常。
4.2 记录日志
在Python中,我们经常需要记录程序的运行日志,以便调试程序。使用装饰器可以很方便地实现这个功能。例如:
import logging
logging.basicConfig(level=logging.INFO, format='%(asctime)s %(levelname)s: %(message)s', datefmt='%Y-%m-%d %H:%M:%S')
def log_info(original_function):
def new_function(*args, **kwargs):
logging.info(f"函数{original_function.__name__}被调用了,参数为:args={args}, kwargs={kwargs}")
result = original_function(*args, **kwargs)
logging.info(f"函数{original_function.__name__}的执行结果为:{result}")
return result
return new_function
@log_info
def my_function(x, y):
return x + y
my_function(1, 2)
上面的代码中,log_info装饰器函数会使用Python的logging模块记录my_function函数的调用参数和执行结果。
4.3 缓存函数计算结果
在Python中,我们经常需要计算一些耗时的函数,如果多次调用同一个函数,它的计算结果可能是相同的,这时我们可以使用缓存来避免重复计算。使用装饰器可以很方便地实现这个功能。例如:
`
import time
import functools
def cache(original_function):
cache_dict = {}
@functools.wraps(original_function)
def new_function(*args, **kwargs):
key = tuple(args) + tuple(sorted(kwargs.items()))
if key in cache_dict:
return cache_dict[key]
else:
result = original_function(*args, **kwargs)
cache_dict[key] = result
return result
return new_function
@cache
def fibonacci(n):
if n == 1 or n == 2:
return 1
else:
return fibonacci(n - 1) + fibonacci(n - 2)
start_time
