欢迎访问宙启技术站
智能推送

Python中的装饰器函数的用途和示例

发布时间:2023-06-05 10:53:40

装饰器(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. 提供日志记录

另一个常见的装饰器应用场景是日志记录,即记录函数调用的输入输出、异常情况、状态变化等信息,并输出到文件、控制台或者其他日志系统。日志记录装饰器可以帮助我们追踪程序状态、排查错误、监控运行情况等,提高程序