Python中的装饰器函数的用途和示例
装饰器(Decorator)是Python中非常强大和重要的概念之一。装饰器本质上是一个可调用对象(function、method或者class),它在不影响原函数(或方法、类)代码和调用方式的情况下,增加或者改变基础函数(或方法、类)的功能。换句话说,装饰器就是用来修饰、增强或者切入原函数接口或行为的一种设计模式。
Python中的装饰器非常灵活和高效,主要基于函数或者类的嵌套、闭包和元编程等特性实现。装饰器可以用于非常多的应用场合,比如提供数据验证、授权、性能分析、日志记录、事务处理、缓存、单例模式、工厂模式等功能。下面就通过实例讲解Python中装饰器函数的用途和示例。
1. 提供数据验证
装饰器在函数调用前或者调用后执行一些额外操作,比如数据校验。考虑如下的任务:编写一个装饰器,用于检查函数参数的类型和范围是否合理。如果不满足要求,就触发异常并提示错误信息。示例代码如下:
def verify_args(func):
def wrapper(*args, **kwargs):
argtypes = func.__annotations__
for i, arg in enumerate(args):
argname = func.__code__.co_varnames[i]
if argname in argtypes and not isinstance(arg, argtypes[argname]):
raise TypeError('Arg \'{}\' type error: should be {}'.format(argname, argtypes[argname]))
for argname, arg in kwargs.items():
if argname in argtypes and not isinstance(arg, argtypes[argname]):
raise TypeError('Arg \'{}\' type error: should be {}'.format(argname, argtypes[argname]))
result = func(*args, **kwargs)
return result
return wrapper
@verify_args
def add(a: int, b: int) -> int:
return a + b
print(add(1, 2)) # should be 3
print(add(1, '2')) # should raise TypeError
print(add(1, b=2.0)) # should raise TypeError
在上述代码中,我们首先定义了一个装饰器函数verify_args,它接受一个基础函数func作为参数,并返回一个wrapper函数。在wrapper函数中,我们首先获取了基础函数func的参数类型注解(即函数声明时使用的冒号后面的类型),并遍历函数调用时的所有位置参数(args)和关键字参数(kwargs)。如果某个参数名称在参数类型注解中出现,并且该参数的类型不符合注解要求,则触发TypeError异常并提示错误信息。否则,wrapper函数就将这些参数以及它们的值传递给基础函数func,并将结果返回。
下面我们以一个计算器函数add为例进行测试。在add函数上面添加@verify_args装饰器,这样调用add函数时通过装饰器就会判断参数类型和范围合法性,从而保证程序的健壮性和安全性。相应地,如果想要定义一个自己的计算器函数mul,只需要复制一份add代码并在上面添加@verify_args装饰器即可,不需要再重复写一遍参数类型检查的代码。
2. 提供授权
另一个常见的装饰器应用场合是提供授权,即只有经过认证或者授权的用户才能执行某些操作。这种装饰器在Web开发中尤其常见,比如控制用户登录、权限管理等。下面我们就例举一个简单的授权装饰器实现过程。
def auth(func):
def wrapper(*args, **kwargs):
user = {'id': 123, 'name': 'foo', 'email': 'foo@gmail.com'} # simulate user data from DB/API
if user: # check if user is authenticated or not
result = func(user, *args, **kwargs)
return result
else:
raise ValueError('Authentication Required') # raise error if user is not authenticated
return wrapper
@auth
def get_user(user, id):
if id == user['id']:
return {'id': user['id'], 'name': user['name']}
else:
raise ValueError('Unauthorized Access')
print(get_user(123)) # should return {'id': 123, 'name': 'foo'}
print(get_user(456)) # should raise ValueError
在上述代码中,我们首先定义一个装饰器函数auth,接受一个基础函数func作为参数,并返回一个wrapper函数。在wrapper函数中,我们模拟了认证过的用户数据(比如来自数据库或者API),并在函数调用前检查用户是否已经通过认证。如果用户认证成功,我们就将用户信息和所有参数一起传递给基础函数func,并将结果返回。如果用户未通过认证,则触发ValueError异常并提示错误信息。
下面我们以一个获取用户信息的函数get_user为例进行测试。在get_user函数上添加@auth装饰器,这样调用get_user函数时通过装饰器就会检查用户是否认证通过,从而保证安全性。相应地,如果想要定义其他需要授权的函数,只需要复制上面的代码并在上面添加@auth装饰器即可。
3. 提供性能分析
另一个重要的装饰器应用场景是性能分析,即统计函数调用的耗时、调用次数、参数情况等指标,并输出日志或者图表。性能分析装饰器可以帮助我们优化程序性能、检查瓶颈和异常情况等,提高程序的性能和可维护性。下面我们就给出一个简单的性能分析装饰器示例。
import time
def profile(func):
def wrapper(*args, **kwargs):
start_time = time.time()
result = func(*args, **kwargs)
end_time = time.time()
print('[Profile] Function \'{}\' called with args {} and kwargs {} took {:.4f} seconds.'.format(
func.__name__, args, kwargs, end_time - start_time))
return result
return wrapper
@profile
def fibonacci(n):
if n in (0, 1):
return n
else:
return fibonacci(n-1) + fibonacci(n-2)
print(fibonacci(30)) # should return 832040
在上述代码中,我们定义了一个装饰器函数profile,它接受一个基础函数func作为参数,并返回一个wrapper函数。在wrapper函数中,我们统计了函数调用开始和结束时的时间点,并计算了函数调用耗时。最后,我们输出了一个日志信息,其中包括了函数名、参数列表、耗时信息等。同时,wrapper函数还返回了函数执行的结果。
下面我们以一个计算斐波那契数列的函数fibonacci为例进行测试。在fibonacci函数上添加@profile装饰器,这样调用fibonacci函数时通过装饰器就会记录函数调用的相关信息,方便我们分析程序的性能。如果想要对其他函数进行性能分析,只需要复制上面的代码并在上面添加@profile装饰器即可。
4. 提供日志记录
另一个常见的装饰器应用场景是日志记录,即记录函数调用的输入输出、异常情况、状态变化等信息,并输出到文件、控制台或者其他日志系统。日志记录装饰器可以帮助我们追踪程序状态、排查错误、监控运行情况等,提高程序
