我的编程空间,编程开发者的网络收藏夹
学习永远不晚

python进阶之装饰器

短信预约 -IT技能 免费直播动态提醒
省份

北京

  • 北京
  • 上海
  • 天津
  • 重庆
  • 河北
  • 山东
  • 辽宁
  • 黑龙江
  • 吉林
  • 甘肃
  • 青海
  • 河南
  • 江苏
  • 湖北
  • 湖南
  • 江西
  • 浙江
  • 广东
  • 云南
  • 福建
  • 海南
  • 山西
  • 四川
  • 陕西
  • 贵州
  • 安徽
  • 广西
  • 内蒙
  • 西藏
  • 新疆
  • 宁夏
  • 兵团
手机号立即预约

请填写图片验证码后获取短信验证码

看不清楚,换张图片

免费获取短信验证码

python进阶之装饰器

一.无参装饰器

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

先看一段简单代码:

1 import time
2 def func():
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 import time
 2 def func():
 3     time.sleep(5)
 4     print('程序正在运行......')
 5 
 6 # 计算时间
 7 def timer():
 8     start = time.time()  # 记录程序开始时间
 9     func()
10     endtime = time.time() # 记录程序运行结束时间
11     print(endtime-start) # 打印程序运行时间
12 timer()

输出:

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

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

import time
def func():
    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 import time
 2 def func():
 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 import time
 2 def func():
 3     time.sleep(5)
 4     print('程序正在运行......')
 5 
 6 # 调用函数
 7 def timer(func):# 函数名字作为函数的参数
 8     def inner():
 9         start = time.time()  # 记录程序开始时间
10         func()
11         endtime = time.time() # 记录程序运行结束时间
12         print(endtime-start) # 打印程序运行时间
13     return inner 
14 func = timer(func)
15 func() # 直接调用函数本身

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

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

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

再看一下执行过程:

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

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

 1 import time
 2 
 3 # 装饰函数
 4 def timer(func):# 函数名字作为函数的参数
 5     def inner():
 6         start = time.time()  # 记录程序开始时间
 7         func()
 8         endtime = time.time() # 记录程序运行结束时间
 9         print(endtime-start) # 打印程序运行时间
10     return inner
11 @timer # 类似 func = timer(func)
12 def func():# 被装饰函数
13     time.sleep(5)
14     print('程序正在运行......')
15 
16 func()# 非被装饰函数,相当于调用inner函数

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

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

二.带返回值的装饰器

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

 1 import time
 2 
 3 # 装饰函数
 4 def timer(func):# 函数名字作为函数的参数
 5     def inner():
 6         start = time.time()  # 记录程序开始时间
 7         func()
 8         endtime = time.time() # 记录程序运行结束时间
 9         print(endtime-start) # 打印程序运行时间
10     return inner
11 @timer
12 # 被装饰函数
13 def func():
14     time.sleep(5)
15     print('程序正在运行......')
16     return '带返回值的装饰器'
17 str = func()
18 print(str)

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

输出:

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

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

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

 1 import time
 2 
 3 # 装饰函数
 4 def timer(func):# 函数名字作为函数的参数
 5     def inner():
 6         start = time.time()  # 记录程序开始时间
 7         str = func()
 8         endtime = time.time() # 记录程序运行结束时间
 9         print(endtime-start) # 打印程序运行时间
10         return str
11     return inner
12 @timer
13 # 被装饰函数
14 def func():
15     time.sleep(5)
16     print('程序正在运行......')
17     return '带返回值的装饰器'
18 str = func()
19 print(str)

输出:

程序正在运行......
5.0006444454193115
带返回值的装饰器

三.被装饰函数带参数

1.单个参数

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

 1 import time
 2 
 3 # 装饰函数
 4 def timer(func):# 函数名字作为函数的参数
 5     def inner(a):
 6         start = time.time()  # 记录程序开始时间
 7         str = func(a)
 8         endtime = time.time() # 记录程序运行结束时间
 9         print(endtime-start) # 打印程序运行时间
10         return str
11     return inner
12 @timer
13 # 被装饰函数
14 def func(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.多个参数

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

 1 import time
 2 
 3 # 装饰函数
 4 def timer(func):# 函数名字作为函数的参数
 5     def inner(a, b):
 6         start = time.time()  # 记录程序开始时间
 7         str = func(a, b)
 8         endtime = time.time() # 记录程序运行结束时间
 9         print(endtime-start) # 打印程序运行时间
10         return str
11     return inner
12 @timer
13 # 被装饰函数
14 def func(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
带返回值的装饰器

3.万能参数装饰器

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

 1 import time
 2 
 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         return str
11     return inner
12 @timer
13 # 被装饰函数
14 def func1(a):
15     time.sleep(5)
16     print('程序正在运行......'+a)
17     print('程序仍在运行......' )
18     return '带返回值的装饰器'
19 @timer
20 # 被装饰函数
21 def func2(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 : 代表的是一个字典,传参数关键字传递

4.固定装饰器

 

1 def timer(func):# 函数名字作为函数的参数
2     def inner(*args, **kwargs):
3          # 执行函数前做的
4         str = func(*args, **kwargs)
5          # 执行函数后做的
6         return str
7     return inner    

四.带参数装饰器

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

 1 import time
 2 def outer(flag):
 3     # 装饰函数
 4     def timer(func):# 函数名字作为装饰器的参数
 5         def inner(*args, **kwargs):
 6             if flag:
 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             return str
14         return inner
15     return timer
16 
17 # 被装饰函数
18 @outer(True) # 带参数的装饰器
19 def func1(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 def func():
2     print('这是一个简单的函数')
3 print(func.__name__) # 获取函数的名称
输出结果:
func

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

 1 import time
 2 
 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         return str
12     return inner
13 @timer
14 # 被装饰函数
15 def func1(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 import time
 2 from functools import wraps
 3 # 装饰函数
 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         return str
13     return inner
14 @timer
15 # 被装饰函数
16 def func1(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.对修改是封闭的

    为什么要对修改封闭呢?

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

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

 

免责声明:

① 本站未注明“稿件来源”的信息均来自网络整理。其文字、图片和音视频稿件的所属权归原作者所有。本站收集整理出于非商业性的教育和科研之目的,并不意味着本站赞同其观点或证实其内容的真实性。仅作为临时的测试数据,供内部测试之用。本站并未授权任何人以任何方式主动获取本站任何信息。

② 本站未注明“稿件来源”的临时测试数据将在测试完成后最终做删除处理。有问题或投稿请发送至: 邮箱/279061341@qq.com QQ/279061341

python进阶之装饰器

下载Word文档到电脑,方便收藏和打印~

下载Word文档

猜你喜欢

python进阶之装饰器

一.无参装饰器问题:如何计算一段程序的运行时间?先看一段简单代码:1 import time2 def func():3 start = time.time() # 记录程序开始时间4 time.sleep(5)5
2023-01-30

python装饰器进阶

目录 装饰器进阶 1. 被装饰的函数有多个参数。 2. 被装饰的函数有返回值 3.在函数中嵌入装饰器 4. 装饰器类 总结
2023-01-30

python装饰器3:进阶

装饰器1:函数装饰器装饰器2:类装饰器装饰器3:进阶函数装饰器装饰方法函数装饰器装饰普通函数已经很容易理解了:@decoratordef func():...#等价于def func():...func = decorator(func)如
2023-01-30

老生常谈Python进阶之装饰器

函数也是对象 要理解Python装饰器,首先要明白在Python中,函数也是一种对象,因此可以把定义函数时的函数名看作是函数对象的一个引用。既然是引用,因此可以将函数赋值给一个变量,也可以把函数作为一个参数传递或返回。同时,函数体中也可以再
2022-06-04

Python之装饰器

在Python中一切皆对象,函数是一等对象。这意味着可以通过名字引用函数。>>> a=123>>> a123>>> name='zeng'>>> name'zeng'>>> def func():...     print "hello!"
2023-01-31

day20-python之装饰器

1.装饰器 1 #!/usr/bin/env python 2 # -*- coding:utf-8 -*- 3 import time 4 def cal(l): 5 start_time=time.time() 6 re
2023-01-31

python之yield与装饰器

防伪码:忘情公子著python中的yield:  在之前发布的《python之列表解析与生成器》中我们有提到过,生成器所实现的是跟列表解析近似的效果,但是我们不能对生成器做一些属于列表解析的操作。  因为生成器本身就不是一个列表,它只是模拟
2023-01-31

Python 语法之装饰器

  装饰器的概念  装饰器是 Python 的一个重要部分。简单地说:就是用于拓展原来函数功能的一种函数,目的是在不改变原函数名(或类名)的情况下,给函数增加新的功能。  这个函数的特殊之处在于它的返回值也是一个函数,这个函数是内嵌 “原”
2023-06-02

python之装饰器(函数)

1. 装饰器   遵循的原则:     开闭原则:   对功能的扩展开放  对代码的修改是封闭# 通用装饰器写法# 存在的意义: 在不破坏原有函数和原有函数调用的基础上,给函数添加新的功能.def wrapper(fn): #
2023-01-30

python进阶之生成器

迭代器  什么叫迭代    可以被for循环的就说明他们是可迭代的,比如:字符串,列表,字典,元祖,们都可以for循环获取里面的数据  下面我们看一个代码:  1 number = 123452 for i in number:3
2023-01-30

详解Python装饰器之@property

一、property() 函数讲解 了解 @property 装饰器之前,我们首先要了解内置函数的 property()。class property(fget=None, fset=None, fdel=None, doc=None)描述
2022-06-02

Python 3 之 装饰器详解

------------ 装饰器 -----------------------------------------------------什么是装饰器装饰器是为函数和类指定管理代码的一种方式。装饰器本身的形式是处理其他的可调用对象的可调用
2023-01-31

python学习系列之python装饰器

一、常规的装饰器,比如 @auth,执行2步操作:1、执行auth函数,并传参func进来2、获取返回值,并赋值给被装饰器的函数的函数名(如让fetch_server_list等于返回值)二、而带参数的装饰器,比如 @auth(before
2023-01-31

python魔术方法之装饰器

三个魔术方法:__get__()__set__()__delete__()object.__get__(self,实例名,owner)    #owner = 属主 ,instance = 属主类owner的实例object.__set__
2023-01-31

Python之函数进阶

一.形参的动态参数: 动态参数,用于参数不确定时用. 格式:def fun (*args)fun(args)1. 动态接收位置参数:动态参数必须在位置参数后.列子1.假如参数不确定时,如下的列子,每人饭量不一样,吃的种类不一样,此时用到动态
2023-01-30

python装饰器2:类装饰器

装饰器1:函数装饰器装饰器2:类装饰器装饰器3:进阶本文是装饰器相关内容的第二篇,关于类装饰器。"类装饰器"有两种解读方式:用来装饰类的装饰器;类作为装饰器装饰其它东西。你如何认为取决于你,两种说法都有出现在其它的文章中。我的文章中是将"类
2023-01-30

Python全栈开发之---装饰器

1、装饰器的形成过程 1 import time 2 3 def func1(): 4 print('in func1') 5 6 def timer(func): 7 def inner(): 8 st
2023-01-30

编程热搜

  • Python 学习之路 - Python
    一、安装Python34Windows在Python官网(https://www.python.org/downloads/)下载安装包并安装。Python的默认安装路径是:C:\Python34配置环境变量:【右键计算机】--》【属性】-
    Python 学习之路 - Python
  • chatgpt的中文全称是什么
    chatgpt的中文全称是生成型预训练变换模型。ChatGPT是什么ChatGPT是美国人工智能研究实验室OpenAI开发的一种全新聊天机器人模型,它能够通过学习和理解人类的语言来进行对话,还能根据聊天的上下文进行互动,并协助人类完成一系列
    chatgpt的中文全称是什么
  • C/C++中extern函数使用详解
  • C/C++可变参数的使用
    可变参数的使用方法远远不止以下几种,不过在C,C++中使用可变参数时要小心,在使用printf()等函数时传入的参数个数一定不能比前面的格式化字符串中的’%’符号个数少,否则会产生访问越界,运气不好的话还会导致程序崩溃
    C/C++可变参数的使用
  • css样式文件该放在哪里
  • php中数组下标必须是连续的吗
  • Python 3 教程
    Python 3 教程 Python 的 3.0 版本,常被称为 Python 3000,或简称 Py3k。相对于 Python 的早期版本,这是一个较大的升级。为了不带入过多的累赘,Python 3.0 在设计的时候没有考虑向下兼容。 Python
    Python 3 教程
  • Python pip包管理
    一、前言    在Python中, 安装第三方模块是通过 setuptools 这个工具完成的。 Python有两个封装了 setuptools的包管理工具: easy_install  和  pip , 目前官方推荐使用 pip。    
    Python pip包管理
  • ubuntu如何重新编译内核
  • 改善Java代码之慎用java动态编译

目录