python进阶装饰器_python进阶之装饰器

本文深入浅出地介绍了Python装饰器的概念、应用场景及其实现方法,包括无参装饰器、带参装饰器、带返回值装饰器等,并探讨了装饰器在保持代码整洁性和灵活性方面的重要作用。

无参装饰器

问题:如何计算一段程序的运行时间?

先看一段简单代码:

1 importtime2 deffunc():3 start = time.time() #记录程序开始时间

4 time.sleep(5)5 print('程序正在运行......')6 endtime = time.time() #记录程序运行结束时间

7 print(endtime-start) #打印程序运行时间

8 #调用函数

9 func()

输出:

程序正在运行......

5.00543737411499

上面的代码是不是就实现了计算程序运行时间的目的,那么如果我想计算别的函数的运行时间是不是也要在函数内部加上start和endtime来计算时间的语句,是不是超级麻烦

下面我们修改一下上面的代码,实现计算任何函数的运行时间:

1 importtime2 deffunc():3 time.sleep(5)4 print('程序正在运行......')5

6 #计算时间

7 deftimer():8 start = time.time() #记录程序开始时间

9 func()10 endtime = time.time() #记录程序运行结束时间

11 print(endtime-start) #打印程序运行时间

12 timer()

输出:

程序正在运行......

5.00543737411499

好了,上面代码就是计算函数func的运行时间,大家是不是发现一个问题:只能计算func()函数的运行时间,那么如果我想计算别的函数的运行时间是不是就需要修改timer内部代码?(修改第9行,调用其他函数),还是很麻烦!再优化一下

importtimedeffunc():

time.sleep(5)print('程序正在运行......')#计算时间

def timer(func):#函数名字作为函数的参数

start = time.time() #记录程序开始时间

func()

endtime= time.time() #记录程序运行结束时间

print(endtime-start) #打印程序运行时间

timer(func)

输出:

程序正在运行......

5.000637531280518

把目标函数的名字传递给timer()是不是就实现了计算任意函数运行时间的目的。只要把目标函数传递给timmer即可。ps:函数名字作为函数的参数怎么理解,自行百度一下-.-!

问题:

问题又来了,如果我们要把这个函数丢给别人使用,别人就要在自己的代码中调用timmer(func)(成百的函数都要计算时间),别人也不愿意。设想一下如果我们想计算我们自己的任意一个函数(如func()函数)的运行时间,只要们直接调用函数本身就自动计算出这个函数的运行时间,是不是就很cool了。

像这样:计算func()函数的运行时间,只需要写func()然后输出执行时间

1 importtime2 deffunc():3 time.sleep(5)4 print('程序正在运行......')5

6 #调用函数

7 def timer(func):#函数名字作为函数的参数

8 start = time.time() #记录程序开始时间

9 func()10 endtime = time.time() #记录程序运行结束时间

11 print(endtime-start) #打印程序运行时间

12 func =timer(func)13 func() #直接调用函数本身

是不是就很方便?那么该如何实现呢?下面接着看代码

1 importtime2 deffunc():3 time.sleep(5)4 print('程序正在运行......')5

6 #调用函数

7 def timer(func):#函数名字作为函数的参数

8 definner():9 start = time.time() #记录程序开始时间

10 func()11 endtime = time.time() #记录程序运行结束时间

12 print(endtime-start) #打印程序运行时间

13 returninner14 func =timer(func)15 func() #直接调用函数本身

我们在timer函数内部添加一个函数用例包裹我们的程序代码,再在timer返回inner函数,就实现了我们的目的,不信?自己运行试试。 其实上面的timer就已经是一个装饰器了。

其实timer函数只是起到了修饰func函数的作用并给自己附加了一个功能,我们再分析一下上面代码的执行过程或原理

红线圈出来的部分其实是一个闭包。且外部函数返回内部函数的函数名字,这样我们就可以使用源函数的函数名字接受这个返回值然后执行inner内部的代码了

再看一下执行过程:

好了,感觉...貌似....好像....还是有点小缺陷,我们在计算任意函数的运行时间时,都要这样:func1 = timer(func1)....func1(),func2(),func3().......funcn(),还是很不理想

并不用担心,其实python提供了一种方法,叫做‘语法糖’,比如这样:

1 importtime2

3 #装饰函数

4 def timer(func):#函数名字作为函数的参数

5 definner():6 start = time.time() #记录程序开始时间

7 func()8 endtime = time.time() #记录程序运行结束时间

9 print(endtime-start) #打印程序运行时间

10 returninner11@timer # 类似 func = timer(func)12 deffunc():# 被装饰函数13 time.sleep(5)14 print('程序正在运行......')15

16 func()# 非被装饰函数,相当于调用inner函数

这样就方便了,可以在你想要计算运行时间的任意函数前添加@timer了。自己动手试试吧

上面我们介绍的是不带参数的装饰器,那么带返回值的装饰器又怎么实现呢?

带返回值的装饰器

看下面的代码思考:这样写是否正确

1 importtime2

3 #装饰函数

4 def timer(func):#函数名字作为函数的参数

5 definner():6 start = time.time() #记录程序开始时间

7 func()8 endtime = time.time() #记录程序运行结束时间

9 print(endtime-start) #打印程序运行时间

10 returninner11 @timer12 #被装饰函数

13 deffunc():14 time.sleep(5)15 print('程序正在运行......')16 return '带返回值的装饰器'

17 str =func()18 print(str)

我们先分析一下输出结果是什么?会不会输出“带返回值的装饰器”这个字符串?

输出:

程序正在运行......5.000662088394165None

很显然并没有输出我们想要的结果,why?

因为函数加了装饰器之后们在调用的时候其实已经不是直接的调用函数的本身,而是调用装饰器中的inner函数来间接的调用被装饰函数,由于inner函数内部是没有返回值的,所以会输出none,修改代码

1 importtime2

3 #装饰函数

4 def timer(func):#函数名字作为函数的参数

5 definner():6 start = time.time() #记录程序开始时间

7 str =func()8 endtime = time.time() #记录程序运行结束时间

9 print(endtime-start) #打印程序运行时间

10 returnstr11 returninner12 @timer13 #被装饰函数

14 deffunc():15 time.sleep(5)16 print('程序正在运行......')17 return '带返回值的装饰器'

18 str =func()19 print(str)

输出:

程序正在运行......

5.0006444454193115

带返回值的装饰器

被装饰函数带参数

单个参数

当我们的被装饰函数是有参数的时候,我们又该如何修改我们的装饰器呢?

1 importtime2

3 #装饰函数

4 def timer(func):#函数名字作为函数的参数

5 definner(a):6 start = time.time() #记录程序开始时间

7 str =func(a)8 endtime = time.time() #记录程序运行结束时间

9 print(endtime-start) #打印程序运行时间

10 returnstr11 returninner12 @timer13 #被装饰函数

14 deffunc(a):15 time.sleep(5)16 print('程序正在运行......'+a)17 return '带返回值的装饰器'

18 str = func('ing')19 print(str)

分析

被装饰函数func(a) 在调用时需要传参数a,那么应该由调用的地方传入参数,就是inner内部第7行代码,那么第7行的代码的参数由哪里来呢?应该由调用func(a)函数的函数inner传入,所以我们应该再inner函数传入一个参数a

输出:

程序正在运行......ing

5.000949859619141

带返回值的装饰器

多个参数

上面修改过的装饰器只能使用在带一个参数的函数上,那么当我们需要在2个参数的被装饰函数上应该如何修改呢? 看下面代码

1 importtime2

3 #装饰函数

4 def timer(func):#函数名字作为函数的参数

5 definner(a, b):6 start = time.time() #记录程序开始时间

7 str =func(a, b)8 endtime = time.time() #记录程序运行结束时间

9 print(endtime-start) #打印程序运行时间

10 returnstr11 returninner12 @timer13 #被装饰函数

14 deffunc(a, b):15 time.sleep(5)16 print('程序正在运行......'+a)17 print('程序仍在运行......' +b)18 return '带返回值的装饰器'

19 str = func('ing', 'ing')20 print(str)

输出:

程序正在运行......ing

程序仍在运行......ing

5.000631809234619

带返回值的装饰器

万能参数装饰器

这样我们就可以把上面这个装饰器应用在带2个参数的函数上了,那么问题来了,如果带3个参数呢或者更多呢,哇!头都大了,那是不是要写好多个一样功能但是参数个数不同的函数了? 答案是否定的 看代码

1 importtime2

3 #装饰函数

4 def timer(func):#函数名字作为函数的参数

5 def inner(*args, **kwargs):6 start = time.time() #记录程序开始时间

7 str = func(*args, **kwargs)8 endtime = time.time() #记录程序运行结束时间

9 print(endtime-start) #打印程序运行时间

10 returnstr11 returninner12 @timer13 #被装饰函数

14 deffunc1(a):15 time.sleep(5)16 print('程序正在运行......'+a)17 print('程序仍在运行......')18 return '带返回值的装饰器'

19 @timer20 #被装饰函数

21 deffunc2(a,b):22 time.sleep(5)23 print('程序正在运行......'+a)24 print('程序仍在运行......' +b)25 return '带返回值的装饰器'

26

27 str = func1('ing')28 print(str)29 print('----------------------------')30 str = func2('ing','ing')31 print(str)

输出:

程序正在运行......ing

程序仍在运行......

5.000674724578857

带返回值的装饰器

----------------------------

程序正在运行......ing

程序仍在运行......ing

5.000207424163818

带返回值的装饰器

是不是挺神奇的,自己动手试试看,是不是可以传任意的参数

ps:这里涉及到参数传递的知识,*args 和**kwargs 代表什么意思? 我这里就简单说一下,详细了解的话自己百度一下把

*args: 代表的是一个元祖,传参时按位置传递

**kwargs: 代表的是一个字典,传参数关键字传递

固定装饰器

1 def timer(func):#函数名字作为函数的参数

2 def inner(*args, **kwargs):3 #执行函数前做的

4 str = func(*args, **kwargs)5 #执行函数后做的

6 returnstr7 return inner

带参数装饰器

现在想一个问题,如果我们有很多个函数已经装饰了timer这个装饰器,那么我又不想要这个功能了,那我们该怎么办呢? 一个一个的删除装饰器嘛?超级麻烦

1 importtime2 defouter(flag):3 #装饰函数

4 def timer(func):#函数名字作为装饰器的参数

5 def inner(*args, **kwargs):6 ifflag:7 start = time.time() #记录程序开始时间

8 str = func(*args, **kwargs) #被装饰函数

9 endtime = time.time() #记录程序运行结束时间

10 print(endtime-start) #打印程序运行时间

11 else:12 str = func(*args, **kwargs)13 returnstr14 returninner15 returntimer16

17 #被装饰函数

18 @outer(True) # 带参数的装饰器19 deffunc1(a):20 time.sleep(1)21 print('程序正在运行......'+a)22 print('程序仍在运行......')23 return 'b'

24 ret = func1('ing')25 print(ret)

这样我们不想使用装饰器的时候只要传入一个False即可

看下输出

启用装饰器:

程序正在运行......ing

程序仍在运行......

1.0000784397125244

b

屏蔽装饰器:

程序正在运行......ing

程序仍在运行......

b

有点晕吗?我们分析一下现在@outer(True) 代表的是什么意思?现在的@outer(True) 其实是分为两个部分的

1.@符号

2.outer(True): 表示纯纯的调用outer这个函数,因为outer函数返回了装饰器函数timer的名字,所以现在应该是这样的:timer = outer(True)

然后我们在连接@符号就编程 @timer 是不是就和之前一样了-> func = timer(func)

这样我们之前提到的问题是不是就轻松解决了呢

继续看一段代码

1 deffunc():2 print('这是一个简单的函数')3 print(func.__name__) # 获取函数的名称

输出结果:

func

现在想一个问题,我们上面写好的装饰器,我要想在函数外部获取被装饰函数的函数名字也这么写会是什么情况?

1 importtime2

3 #装饰函数

4 def timer(func):#函数名字作为函数的参数

5 def inner(*args, **kwargs):6 print(*args)7 start = time.time() #记录程序开始时间

8 str = func(*args, **kwargs)9 endtime = time.time() #记录程序运行结束时间

10 print(endtime-start) #打印程序运行时间

11 returnstr12 returninner13 @timer14 #被装饰函数

15 deffunc1(a):16 #time.sleep(5)

17 print('程序正在运行......'+a)18 print('程序仍在运行......')19 return '带返回值的装饰器'

20

21

22 str = func1('ing')23 print(str)24 print(func1.__name__)

想一下,最后一行代码会不会输出:func1:

输出:

ing

程序正在运行......ing

程序仍在运行......

0.0

带返回值的装饰器

inner

其实输出的是inner函数名(不理解的可以往前看,前面说过),那么我还是想输出func1的名字,该怎么实现呢?其实python自带了一个装饰器可以很好的解决这个问题

1 importtime2 from functools importwraps3 #装饰函数

4 def timer(func):#函数名字作为函数的参数

5 @wraps(func)6 def inner(*args, **kwargs):7 print(*args)8 start = time.time() #记录程序开始时间

9 str = func(*args, **kwargs)10 endtime = time.time() #记录程序运行结束时间

11 print(endtime-start) #打印程序运行时间

12 returnstr13 returninner14 @timer15 #被装饰函数

16 deffunc1(a):17 #time.sleep(5)

18 print('程序正在运行......'+a)19 print('程序仍在运行......')20 return '带返回值的装饰器'

21 str = func1('ing')22 print(str)23 print(func1.__name__)

输出:

ing

程序正在运行......ing

程序仍在运行......

0.0

带返回值的装饰器

func1

wraps(func) 装饰器的作用就是不改变被装饰函数任何作用

总结

什么是装饰器

装饰器本质上就是一个python函数,他可以让其他函数在不需要做任何代码变动的前提下,增加额外的功能,装饰器的返回值也是一个函数对象。

何时使用装饰器

不想修改函数的调用方式,但是还想在原来的函数前后添加功能(实际公司项目中如果项目已经完成,但是不想再修改原代码,我们就可以使用装饰器)

原则

开放封闭原则

1.开放原则:对扩展是开放的

为什么要对扩展开放呢?

我们说,任何一个程序,不可能在设计之初就已经想好了所有的功能并且未来不做任何更新和修改。所以我们必须允许代码扩展、添加新功能。

2.对修改是封闭的

为什么要对修改封闭呢?

就像我们刚刚提到的,因为我们写的一个函数,很有可能已经交付给其他人使用了,如果这个时候我们对其进行了修改,很有可能影响其他已经在使用该函数的用户。

装饰器完美的遵循了这个开放封闭原则。

内容概要:本文围绕【卡尔曼滤波】具有梯度流的一类系统的扩散映射卡尔曼滤波器研究(Matlab代码实现)“具有梯度流的一类系统的扩散映射卡尔曼滤波器研究”展开,重点介绍了一种结合扩散映射与卡尔曼滤波的新型滤波方法,适用于存在模型不确定性或混沌特征的动态系统状态估计。该方法利用梯度流信息提升滤波性能,在可预测性较高的阶段对混沌系统具备一定的预测能力,并通过Matlab代码实现验证其有效性。文档还附带多个相关研究主题,涵盖故障诊断、路径规划、信号处理、无人机控制、电力系统优化等多个领域,展示了卡尔曼滤波及其他先进算法在工程实践中的广泛应用。; 适合人群:具备一定数学基础和编程能力,从事控制理论、信号处理、自动化、航空航天、机器人或相关工程领域的研究生、科研人员及工程师。; 使用场景及目标:①研究复杂动态系统(如混沌系统)的状态估计与预测问题;②提升在模型不准确或噪声干扰严重情况下的滤波精度;③结合Matlab仿真平台开展算法开发与验证,推动理论成果向实际应用转化; 阅读建议:建议读者结合提供的Matlab代码进行实践操作,深入理解扩散映射与卡尔曼滤波的融合机制,同时可参考文中列举的多种应用场景拓展思路,注重算法原理与工程实现的结合。
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值