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

Python装饰器函数的基本使用方法

发布时间:2023-06-14 08:56:22

Python装饰器函数的基本使用方法

不少初学Python的小伙伴在接触装饰器时感觉比较抽象,不知道该如何入手。本文试图用简单易懂的方式,帮助各位读者掌握Python装饰器函数的基本使用方法。

首先需要了解的是,Python装饰器是一种语法糖(Syntactic Sugar),即一种让代码更美观易懂的语法,而非实际的功能。Python装饰器函数的作用是在不改变原函数代码的情况下为其添加额外功能。

想要理解装饰器的使用方法,需要从最简单的装饰器开始学习。下面用一个简单的例子来介绍如何编写一个装饰器。

例1:获取函数运行时间

1    import time
2    
3    def timeit(func):
4        def wrapper():
5            start = time.clock()
6            func()
7            end = time.clock()
8            print('used:', end - start)
9        return wrapper
10   
11   @timeit
12   def test():
13       time.sleep(1)

这个装饰器函数的作用是统计被装饰函数的运行时间。为了使代码易于理解,我们将这个装饰器函数分为三个部分。

部分

3    def timeit(func):

装饰器函数的定义语句,注意这里的func是被装饰函数的引用。装饰器函数在定义时需要接受一个函数作为参数。

第二部分

4        def wrapper():
5            start = time.clock()
6            func()
7            end = time.clock()
8            print('used:', end - start)

这一部分定义了这个装饰器真正的逻辑。在这个例子中,wrapper函数记录被装饰函数的开始运行时间,运行被装饰函数,记录结束时间,最后输出运行时间。

第三部分

9        return wrapper

这一部分,装饰器函数将具体的实现部分进行封装,返回函数。

现在我们可以将一个函数标记为要使用这个装饰器,这样被标记的函数调用时就会自动执行这个装饰器函数:

11   @timeit
12   def test():
13       time.sleep(1)

装饰器类似于函数调用,在函数定义时使用@符号进行修饰。当我们调用被装饰的函数时,Python解释器会自动把它转换成如下形式:

test = timeit(test)

这样就可以使用被装饰的函数了。

接下来,我们给大家继续介绍几个比较常用的Python装饰器。

例2:检查函数输入

1    from functools import wraps
2    
3    def check_args(func):
4        @wraps(func)
5        def wrapper(*args, **kwargs):
6            for i in args:
7                if isinstance(i, (int, float, complex)) == False:
8                    raise ValueError('argument type must be int, float or complex')
9            for i in kwargs.values():
10               if isinstance(i, (int, float, complex)) == False:
11                   raise ValueError('argument type must be int, float or complex')
12           return func(*args, **kwargs)
13       return wrapper
14   
15   @check_args
16   def add(a, b, c=0):
17       return a + b + c

这个装饰器函数的作用是在运行被装饰函数之前检查输入参数类型是否正确,如果错误则抛出ValueError异常。

这里需要使用一个Python内置的装饰器:functools.wraps。这个装饰器的作用是将函数的属性(如__name__等)复制到一个包装函数中。这样有助于将装饰器与面向对象的编程结合起来同时确保不会影响被调用函数的属性。

要注意的是,在wrapper函数中,我们对各个参数进行类型检查后再调用被装饰函数。如果没有这个检查功能,那么在add函数中输入非数字类型的参数时,会报TypeError异常。

例3:重试装饰器

1    import time
2    
3    def retry(retries):
4        def deco_retry(func):
5            def wrapper(*args, **kwargs):
6                for i in range(retries):
7                    try:
8                        return func(*args, **kwargs)
9                    except Exception as e:
10                       print(e)
11                       time.sleep(1)
12               raise RuntimeError('after {} retries, still failed!'.format(retries))
13           return wrapper
14       return deco_retry
15   
16   @retry(3)
17   def connect():
18       raise ConnectionError('connect failed')

这个装饰器函数的作用是在重试指定次数后,如果还是失败,则抛出异常。其中,装饰器retry接受一个参数retries,表示重试次数。deco_retry是一个函数,返回wrapper函数,前者的作用是装饰被装饰函数,后者的作用是实际执行装饰器函数的逻辑。

wrapper函数中我们使用python中的try-except语句,即“捕获异常”的方式来处理函数的执行问题。当retry达到指定重试次数仍未成功,则抛出RuntimeError异常。

通过上面三个例子的介绍,相信各位读者已经理解了Python装饰器函数的基本使用方法和常见场景。除了上述场景以外,装饰器函数还有很多其他的使用场景,如缓存、权限控制等。不过,无论用途如何,装饰器函数的本质其实都是将其他函数作为参数,然后将其他函数转换为新函数,最后返回一个适用于原始函数的新函数。掌握了装饰器函数的使用方法,大大提高了Python编程的效率,也使得代码更加简洁清晰。