首页
学习
活动
专区
工具
TVP
发布
精选内容/技术社群/优惠产品,尽在小程序
立即前往

Python3中的函数装饰器

1:   函数装饰器

  在介绍函数装饰器之前,我们来看下下面的一个小需求:对功能相同但不同算法实现的两个函数的运行时间的比较,或者业务代码中要对某些函数执行时间的统计。下面以判断某个整数是否为素数为例:

判断素数代码如下:

import timefrom math import sqrt def isPrimes1(n): if n return False for i in range(2, int(sqrt(n) + 1)): if n % i == 0: return False return True def isPrimes2(n): if n > 1: if n == 2: return True if n % 2 == 0: return False for x in range(3, int(sqrt(n) + 1), 2): if n % x == 0: return False return True return False

低级版本:在调用之前记下开始时间,调用结束时,使用当前时间减去开始时间,代码如下:

import timestart_time = time.time()print(isPrimes1(32212254719))print('isPrimes1(32212254719) 运行时间:',time.time() - start_time) print('*'*30)start_time = time.time()print(isPrimes2(32212254719))print('isPrimes2(32212254719) 运行时间:',time.time() - start_time)

按照这种方式来统计的话,如果实际业务中有N多个地方调用了,你就得修改N多次(最要命的是一不小心,你还能整出bug来),并且代码都是一样的,也就是冗余的。我们之前有学过函数,即对相同功能的封装,那么下面就用函数来改进。

普通函数版本:

def outer(f,*args): start_time = time.time() ret = f(*args) print(f.__name__ + '('+ str(*args) + ') 运行时间:',time.time() - start_time) return ret print(outer(isPrimes1,32212254719))print(outer(isPrimes2,32212254719))

虽然解决了冗余问题,但是还得去修改N多地方,而且函数的调用方式也改变了,这样还是有风险的,还是在做重复的事情。那还有没有优化的空间呢?在前面有学习过Python中的闭包,即能够保存外层变量的状态,下面就用闭包来改造它:

闭包版本:

def outer(f): def inner(*args): start_time = time.time() ret = f(*args) # f对inner来说,属于外层变量,在inner中能够访问它 print(f.__name__ + '('+ str(*args) + ') 运行时间:',time.time() - start_time) return ret return inner isPrimes1 = outer(isPrimes1)isPrimes2 = outer(isPrimes2) isPrimes1(32212254719)isPrimes2(32212254719)

上面这个版本其实就是我们今天要学习的装饰器函数,它既不改变被装饰函数的调用方式,也不改变被装饰函数中内部的代码,只是给被装饰函数添加了额外的功能。

但是上面的版本,还是不支持关键字参数传递。

闭包版本2:

def outer(f): def inner(*args,**kwargs): start_time = time.time() ret = f(*args,**kwargs) # f对inner来说,属于外层变量,在inner中能够访问它 print(f"{f.__name__}运行时间:{time.time() - start_time} ") return ret return inner isPrimes1 = outer(isPrimes1)isPrimes2 = outer(isPrimes2) isPrimes1(n=32212254719)isPrimes2(32212254719)

这个就是函数装饰器,哪个函数需要装饰,只要在被装饰函数定义后面调用装饰器outer函数,把被装饰函数重新绑定到outer函数的返回对象;目前这一操作我们是通过自己手工添加代码实现的,在Python中提供了装饰器字符: "@"符号,在函数定义前面使用@outer,Python会自动帮我们完成手工赋值的代码。完整代码如下:

import timefrom math import sqrt def outer(f): def inner(*args,**kwargs): start_time = time.time() ret = f(*args,**kwargs) # f对inner来说,属于外层变量,在inner中能够访问它 print(f"{f.__name__}运行时间:{time.time() - start_time} ") return ret return inner @outer # 相当于isPrimes1 = outer(isPrimes1),这个赋值python会自动完成,不需要我们自己赋值def isPrimes1(n): if n return False for i in range(2, int(sqrt(n) + 1)): if n % i == 0: return False return True @outer # 相当于isPrimes2 = outer(isPrimes2),这个赋值python会自动完成,不需要我们自己赋值def isPrimes2(n): if n > 1: if n == 2: return True if n % 2 == 0: return False for x in range(3, int(sqrt(n) + 1), 2): if n % x == 0: return False return True return False # isPrimes1 = outer(isPrimes1)# isPrimes2 = outer(isPrimes2) isPrimes1(n=32212254719)isPrimes2(32212254719)

2:带参数的装饰器

当我们需要关闭时间统计的时候,我们可以把@outer注释掉,也可以给outer传递参数来实现关闭:

import timefrom math import sqrt def outer_o(flag): def outer(f): def inner(*args,**kwargs): if flag: start_time = time.time() ret = f(*args,**kwargs) # f对inner来说,属于外层变量,在inner中能够访问它 if flag: print(f"{f.__name__}运行时间:{time.time() - start_time} ") return ret return inner return outer # 相当于isPrimes1 = outer_o(True) (isPrimes1),这个赋值python会自动完成,不需要我们自己赋值# 注意是@outer_o(True) 而不是@outer_o;outer_o(True)会返回outer函数对象,由于是闭包,最里面的inner函数能够访问它外面的变量;@outer_o(True) def isPrimes1(n): if n return False for i in range(2, int(sqrt(n) + 1)): if n % i == 0: return False return True @outer_o(True) # 相当于isPrimes2 = outer_o(True)(isPrimes2),这个赋值python会自动完成,不需要我们自己赋值def isPrimes2(n): if n > 1: if n == 2: return True if n % 2 == 0: return False for x in range(3, int(sqrt(n) + 1), 2): if n % x == 0: return False return True return False # isPrimes1 = outer(isPrimes1)# isPrimes2 = outer(isPrimes2) print(isPrimes1(32212254719))print(isPrimes2(32212254719)

当关闭时间统计的时候,我们可以把@outer_o(True)   修改为@outer_o(False)

3:多个装饰器

函数可以有多个装饰器,代码如下:

def outer_1(f): def inner(*args,**kwargs): print('in outer_1 start') ret = f(*args,**kwargs) print('in outer_1 end') return ret return inner def outer_2(f): def inner(*args,**kwargs): print('in outer_2 start') ret = f(*args,**kwargs) print('in outer_2 end') return ret return inner # ......"""# 装饰从最靠近函数定义的地方开始,outer_2 首先对原始函数f1进行装饰,outer_1 对outer_2装饰之后返回的结果进行装饰;装饰器执行顺序:最靠近函数定义的地方最后执行;"""@outer_1 # f1 = outer_1(outer_2(f1) )@outer_2 # f1 = outer_2(f1)def f1(a): print('in f1 ',a) f1(100) '''输出结果:in outer_1 startin outer_2 startin f1 100in outer_2 endin outer_1 end'''

除了函数装饰器,还有类装饰器,类装饰器在后面介绍。

  • 发表于:
  • 原文链接https://kuaibao.qq.com/s/20200803A03PV900?refer=cp_1026
  • 腾讯「腾讯云开发者社区」是腾讯内容开放平台帐号(企鹅号)传播渠道之一,根据《腾讯内容开放平台服务协议》转载发布内容。
  • 如有侵权,请联系 cloudcommunity@tencent.com 删除。

扫码

添加站长 进交流群

领取专属 10元无门槛券

私享最新 技术干货

扫码加入开发者社群
领券