1. 装饰器的概念
Python中的装饰器是一种函数,它可以装饰其他函数,以修改或增强其行为。简单来说,装饰器就是在不改变原函数代码的情况下,增加功能,实现代码的复用。
# 一个简单的装饰器示例
def my_decorator(some_function):
def wrapper():
print("Something is happening before some_function() is called.")
some_function()
print("Something is happening after some_function() is called.")
return wrapper
def say_hello():
print("Hello!")
say_hello = my_decorator(say_hello)
say_hello() # 输出:Something is happening before some_function() is called. Hello! Something is happening after some_function() is called.
2. 装饰器的语法糖(@)
Python提供了装饰器的语法糖,使用@符号可以更方便地实现装饰器功能。
# 一个实现计时功能的装饰器示例
import time
def time_it(func):
def wrapper(*args, **kwargs):
start_time = time.time()
result = func(*args, **kwargs)
end_time = time.time()
print(f"函数 {func.__name__} 的运行时间为 {end_time - start_time} 秒")
return result
return wrapper
@time_it
def do_something():
time.sleep(2)
print("任务完成!")
do_something()
# 输出(运行时间可能有所不同):
# 任务完成!
# 函数 do_something 的运行时间为 2.002633571624756 秒
3. 装饰器的应用场景
3.1 函数执行前的操作
装饰器可以在函数执行之前执行某些额外操作,比如参数检查、权限验证等。
# 参数检查装饰器示例
def check_arg_types(*expected_args):
def decorator(func):
def wrapper(*args, **kwargs):
for index, arg in enumerate(args):
if not isinstance(arg, expected_args[index]):
raise TypeError(f"The {index + 1}th argument should be {expected_args[index]}, but {type(arg)} is given.")
for key, value in kwargs.items():
if not isinstance(value, expected_args[len(args) + list(kwargs.keys()).index(key)]):
raise TypeError(f"The argument {key} should be {expected_args[len(args) + list(kwargs.keys()).index(key)]}, but {type(value)} is given.")
return func(*args, **kwargs)
return wrapper
return decorator
@check_arg_types(int, int)
def add(a, b):
return a + b
add(1, "2") # 报错:TypeError: The 2th argument should be , but is given.
3.2 函数执行后的操作
装饰器也可以在函数执行之后执行某些额外操作,比如记录日志、发送Email等。
# 日志记录装饰器示例
def log_it(func):
import time
def wrapper(*args, **kwargs):
start_time = time.time()
result = func(*args, **kwargs)
end_time = time.time()
with open("log.txt", "a") as f:
f.write(f"函数 {func.__name__} 执行时间为 {end_time - start_time} 秒,结果为 {result}\n")
return result
return wrapper
@log_it
def square(x):
return x ** 2
square(3)
# 日志文件 log.txt 中记录了:
# 函数 square 执行时间为 4.0531158447265625e-06 秒,结果为 9
3.3 缓存数据
装饰器可以实现函数的结果缓存,避免重复执行同样的计算。
# 缓存数据装饰器示例
def memoize(func):
cache = {}
def wrapper(*args):
if args in cache:
print(f"从缓存中读取结果 {cache[args]}")
return cache[args]
result = func(*args)
cache[args] = result
print(f"将结果 {result} 缓存起来")
return result
return wrapper
@memoize
def fibonacci(n):
if n == 0 or n == 1:
return n
return fibonacci(n - 1) + fibonacci(n - 2)
fibonacci(10)
# 输出:
# 将结果 55 缓存起来
4. 多个装饰器的使用
使用多个装饰器可以实现对函数的多个增强功能。装饰器的执行顺序是由下至上的。
# 多个装饰器的使用示例
def decorator1(func):
def wrapper():
print("Decorator 1 before...")
func()
print("Decorator 1 after...")
return wrapper
def decorator2(func):
def wrapper():
print("Decorator 2 before...")
func()
print("Decorator 2 after...")
return wrapper
@decorator1
@decorator2
def say_hello():
print("Hello!")
say_hello()
# 输出:
# Decorator 1 before...
# Decorator 2 before...
# Hello!
# Decorator 2 after...
# Decorator 1 after...
5. 带参数的装饰器
装饰器也可以带参数,例如在时间计算中可以指定时间单位。
# 带参数的装饰器示例
def time_it(unit="second"):
def decorator(func):
def wrapper(*args, **kwargs):
start_time = time.time()
result = func(*args, **kwargs)
end_time = time.time()
if unit == "second":
print(f"函数 {func.__name__} 的运行时间为 {end_time - start_time} 秒")
elif unit == "millisecond":
print(f"函数 {func.__name__} 的运行时间为 {(end_time - start_time) * 1000} 毫秒")
else:
raise ValueError("Invalid unit.")
return result
return wrapper
return decorator
@time_it(unit="millisecond")
def do_something():
time.sleep(2)
print("任务完成!")
do_something()
# 输出(运行时间可能有所不同):
# 任务完成!
# 函数 do_something 的运行时间为 2002.8650760650635 毫秒
6. 注意事项
装饰器可以增强函数的功能,但是也有一些需要注意的细节问题。
装饰器会改变函数的元信息,如函数名、文档字符串、注解等。可以使用functools库中的wraps函数来解决元信息问题。
装饰器不适合用于修改函数的返回值,因为修改返回值会在函数调用后执行,而装饰器是在函数调用前执行的。如果需要修改返回值,应该在函数内部进行相应的处理。
装饰器不适合用于不确定参数个数的函数,因为装饰器的参数个数要和被装饰函数的参数个数相同。