Python装饰器详解:自定义函数增强的高级技巧
Python装饰器是一种高级技巧,它可以让我们在不改变函数原有逻辑的情况下,为函数添加新的功能或者改变函数的行为。装饰器本身就是一个函数,它接受一个函数对象作为参数,并返回一个新的函数对象,新的函数对象可能会在执行前或者执行后添加新的操作。本文将详细介绍Python装饰器的用法和实现。
1. 装饰器的基本用法
1) 装饰器的语法
Python装饰器采用了函数嵌套(closure)的概念,在定义一个装饰器函数时,需要在函数内部再定义一个函数,这里的内部函数就是装饰器函数。
装饰器函数的语法如下:
def decorator(func):
def wrapper(*args, **kwargs):
#操作1
result = func(*args, **kwargs)
#操作2
return result
return wrapper
这里的decorator就是装饰器函数,它有一个参数func,它是被修饰的函数对象。在decorator函数内部,定义了一个新的函数wrapper,它的作用是在被修饰函数func执行前或执行后添加一些新的操作。
wrapper函数有两个参数*args和**kwargs,它们代表传递给被修饰函数func的任意位置参数和关键字参数,这样就可以支持任意类型的函数。
最后,decorator函数返回wrapper函数对象,这个函数对象可以覆盖原有的函数对象,从而实现对原有函数的扩展。
2) 装饰器的应用场景
Python装饰器广泛应用于日志记录、性能分析、缓存、权限校验等方面,具体用法如下:
- 日志记录
如下示例代码:
def log(func):
def wrapper(*args, **kwargs):
print(f'{func.__name__} called with args={args}, kwargs={kwargs}')
return func(*args, **kwargs)
return wrapper
@log
def add(a, b):
return a + b
add(3, 4) # output: add called with args=(3, 4), kwargs={}
这里的log函数就是一个装饰器函数,它将原有的add函数修饰后,每次调用add函数时,都会记录函数名、位置参数和关键字参数的信息。
- 性能分析
如下示例代码:
import time
def timeit(func):
def wrapper(*args, **kwargs):
start = time.time()
result = func(*args, **kwargs)
end = time.time()
print(f'{func.__name__} took {end - start} seconds')
return result
return wrapper
@timeit
def fib(n):
if n == 0:
return 0
elif n == 1:
return 1
else:
return fib(n-1) + fib(n-2)
fib(30) # output: fib took 0.3063042163848877 seconds
这里的timeit函数就是一个装饰器函数,它可以测量函数执行的时间,并输出执行时间的信息。
- 缓存
如下示例代码:
import functools
def cache(func):
cache = {}
@functools.wraps(func)
def wrapper(*args, **kwargs):
key = (args, tuple(sorted(kwargs.items())))
if key in cache:
print('cache hit')
return cache[key]
else:
print('cache miss')
result = func(*args, **kwargs)
cache[key] = result
return result
return wrapper
@cache
def add(a, b):
return a + b
add(2, 3) # output: cache miss
add(2, 3) # output: cache hit
这里的cache函数就是一个缓存函数,它可以缓存函数的输入和输出,避免重复计算。
- 权限校验
如下示例代码:
def login_required(func):
def wrapper(*args, **kwargs):
if 'user' in kwargs and kwargs['user']:
return func(*args, **kwargs)
else:
print('You are not authorized!')
return wrapper
@login_required
def add(a, b, user=None):
return a + b
add(1, 2, user='Alice') # output: 3
add(3, 4) # output: You are not authorized!
这里的login_required函数就是一个权限校验函数,它可以根据用户信息授权,只允许已登录用户访问被修饰的函数。如果没有用户信息,则返回未授权的提示信息。
2. 装饰器的高级用法
1) 带参数的装饰器
有些情况下,装饰器可能需要接受一些参数,因此,需要定义一个新的函数来生成装饰器本身。这个新的函数通常称为“装饰器生成器”。
如下示例代码:
def repeat(num_times):
def decorator(func):
@functools.wraps(func)
def wrapper(*args, **kwargs):
for i in range(num_times):
result = func(*args, **kwargs)
return result
return wrapper
return decorator
@repeat(num_times=3)
def greet(name):
print(f'Hello, {name}')
greet('Alice') # output: Hello, Alice (printed 3 times)
这里的repeat函数是一个装饰器生成器,它接受一个整数参数num_times,并返回一个装饰器函数。decorator函数就是一个装饰器,它将被修饰函数执行多次,实现了greet函数的重复调用。
2) 多个装饰器的叠加
有些情况下,一个函数可能需要被多个装饰器叠加起来使用。可以将多个装饰器按照从上往下的顺序,分别应用于被修饰函数。
如下示例代码:
def debug(func):
def wrapper(*args, **kwargs):
print(f'Calling {func.__name__} with args={args}, kwargs={kwargs}')
return func(*args, **kwargs)
return wrapper
def timer(func):
def wrapper(*args, **kwargs):
start = time.time()
result = func(*args, **kwargs)
end = time.time()
print(f'{func.__name__} took {end - start} seconds')
return result
return wrapper
@debug
@timer
def add(a, b):
return a + b
add(3, 4) # output: Calling add with args=(3, 4), kwargs={}
add took 1.90734863281e-06 seconds
这里的debug和timer函数都是装饰器函数,当这两个装饰器按照从上往下的顺序应用时,先应用debug装饰器,在输出调用信息后,将被修饰的函数传递给timer装饰器,计算函数执行时间后返回结果。
3) 类作为装饰器
虽然装饰器函数是Python解释器支持的语言特性,但有时候,我们可能需要更灵活的装饰器方式,比如使用类来实现装饰器功能。
如下示例代码:
`
class log:
def __init__(self, func):
self.func = func
def __call__(self, *args, **kwargs):
print(f'{self.func.__name__} called with args={args}, kwargs={kwargs}')
return self.func(*args, **kwargs)
@log
def add(a, b):
return a + b
add(3, 4) # output: add called with args=(3,
