Python小白学习之路(二十四)—【装饰器】
装饰器
一、装饰器的本质
装饰器的本质就是函数,功能就是为其他函数添加附加功能。
利用装饰器给其他函数添加附加功能时的原则:
1.不能修改被修饰函数的源代码
2.不能修改被修饰函数的调用方式
举例:计算以下一段程序执行时间
#程序:计算0—19的和
def cal(l):
res = 0
for i in l:
res += i
return res
print(cal(range(20)))
#给上述程度增加时间模块
import time
def cal(l):
start_time = time.time()
res = 0
for i in l:
time.sleep(0.1)
res += i
stop_time = time.time()
print('函数的运行时间是%s'%(stop_time - start_time))
return res
print(cal(range(20))) #执行结果
函数的运行时间是2.0001144409179688
190
#上述增加的时间模块
违反了开放封闭原则,改变了cal()函数的源代码,不是实现装饰器的功能。
因此可以把统计时间的函数单独写出来。并且满足以上两个原则
二、怎么样实现一个基本的装饰器(装饰器的知识储备)
装饰器 = 高阶函数 + 函数嵌套 + 闭包
1.高阶函数
- 函数接受的参数是一个函数名
- 函数的返回值是一个函数名
- 满足上述条件的任何一个都可以是高阶函数
高阶函数类型一:函数接受的参数是一个函数名
#举例:
def name1(n):
print(n)
n('xhg')
def name2(name):
print('my name is %s' %name)
name1(name2)
#执行结果
<function name2 at 0x00E194F8>
my name is xhg
#name1()是一个高阶函数,其接受的参数是函数名name2
#程序分析
#函数名name2是函数name2的内存地址。传给函数name1,即参数n=函数name2的内存地址,因此打印的结果为函数name2的内存地址
#n('xhg'),只执行name2('xhg')
利用‘函数接受的参数是一个函数名’这个思想,为函数name2()添加一个统计时间的功能
import time
def name1(n):
start_time = time.time()
n('xhg')
stop_time = time.time()
print('函数的运行时间是%s'%(stop_time - start_time))
def name2(name):
time.sleep(2)
print('my name is %s' %name)
name1(name2) #执行结果
my name is xhg
函数的运行时间是2.0001144409179688
#上述程序虽然实现了增加时间模块功能,虽然不改变函数name2()的代码,但是改变了函数name()的调用方式
#因此函数name1()不是装饰器
高阶函数类型二:函数的返回值是一个函数名
#举例:
def name1():
print('from name1')
def name2():
print('from name2')
return name1
n = name2()
n() #执行结果
from name2
from name1
#程序分析:
#函数name2()中的返回值中包含函数名name1,所以函数name2()为高阶函数
#将函数名name1赋值给变量n,函数名为该函数内存地址
#n()为执行函数name1
利用‘函数的返回值是一个函数名’这个思想,为函数name2()添加一个统计时间的功能
import time
def name1(n):
start_time = time.time()
n('xhg')
stop_time = time.time()
print('函数的运行时间是%s' % (stop_time - start_time))
return n
def name2(name):
time.sleep(2)
print('my name is %s' %name)
name2 = name1(name2)
name2('xhg') #执行结果
my name is xhg
函数的运行时间是2.0001144409179688
my name is xhg
#程序分析:计算函数name2()运行时间的函数模块name1,没有函数name2()改变调用方式,也没有改变数name2()的源代码。
#但是多执行了一次,该装饰器设计不合格
#结论:单独利用高阶函数无法实现装饰器的功能
2.函数嵌套
函数嵌套实际上就是在函数中又定义了一个函数
#举例: def first():
print('from first ')
def second():
print('from second')
def third():
print('from third')
third()
second()
first() #执行结果
from first
from second
from third
#程序分析

3.闭包
关于闭包这块,我没有太理解了。以下的知识摘自这个博文,对理解闭包挺有帮助的。
https://www.cnblogs.com/guobaoyuan/articles/6756763.html
闭包:首先必须是内部定义的函数,该函数包含对外部作用域而不是全局作用域名字的引用
定义:内部函数的代码包含对外部函数的代码的引用,但一定不是对全局作用域的引用
闭包的基本形式是:
在函数F1中,定义F2,F2只能引用F1定义的变量,之后F1函数返回F2的函数名字
这样就保证了可以将F1的执行结果赋予给一个变量,该变量可以在之后的任何时刻随时可以运行
#举例理解:
x = 1000
def f1():
x = 1
def f2():
print(x)
return f2
f = f1()
print(f)
f()
x = 123 #执行结果
<function f1.<locals>.f2 at 0x003394B0>
1
#顺便提一句,要想充分理解这边程序执行的情况,需要对作用域以及变量那边有清楚的认识
使用闭包的好处:自带状态即变量,可以不用传参就用,方便。
- 闭包(closure)是函数式编程的重要的语法结构。
- 不同的语言实现闭包的方式不同。
- Python以函数对象为基础,为闭包这一语法结构提供支持的
- (我们在特殊方法与多范式中,已经多次看到Python使用对象来实现一些特殊的语法)。
- Python一切皆对象,函数这一语法结构也是一个对象。
- 在函数对象中,我们像使用一个普通对象一样使用函数对象,比如更改函数对象的名字,或者将函数对象作为参数进行传递。
三、装饰器的框架
有了以上三个知识为基础铺垫,我们可以搭出来一个装饰器的框架模型
以计算程序执行时间的功能函数为例
def timmer(func)
def wrapper():
func()
return wrapper
根据上述框架来写一个计算程序执行时间的功能的装饰器
import time
def timmer(func):
def wrapper():
start_time = time.time()
func()
stop_time = time.time()
print('程序运行时间是%s' % (stop_time - start_time))
return wrapper
def test():
time.sleep(3)
print('test程序运行完毕')
test = timmer(test)
test() #执行结果
test程序运行完毕
程序运行时间是3.000171661376953
#程序分析

#timmer(test) 将test()函数的函数名传给timmer函数,实际上是传递的是test的地址
#执行timmer函数的结果是得到wrapper的地址,即test=wrapper的地址
#test()实际上是在执行wrapper函数
注意:@timmer <===> test = timmer(test)
所以,完美的装饰器诞生啦!!!
import time
def timmer(func):
def wrapper():
start_time = time.time()
func()
stop_time = time.time()
print('程序运行时间是%s' % (stop_time - start_time))
return wrapper
@timmer
def test():
time.sleep(3)
print('test程序运行完毕')
test()
四、带返回值的装饰器
#对上面写的这个程序进行一个小小的加工,使得该装饰器有返回值
import time
def timmer(func):
def wrapper():
start_time = time.time()
res = func()
stop_time = time.time()
print('程序运行时间是%s' % (stop_time - start_time))
return res
return wrapper
@timmer
def test():
time.sleep(3)
print('test程序运行完毕')
return '这是test函数的返回值'
res = test()
print(res) #执行结果
test程序运行完毕
程序运行时间是3.0001718997955322
这是test函数的返回值
#程序理解
#该程序的理解重点还是要清楚 test() 这一步执行的是哪个函数,分析同上,实际执行 wrapper() 函数
#res = func() 实际执行test()函数,并将test函数的返回值赋值给res变量
#wrapper函数执行完,将局部变量res的值通过return返回给全局变量res
五、带可变长度参数的装饰器
#回顾:
- 参数组(非固定长度的参数)
- *args 针对位置参数传递
- **kwargs 针对关键字参数传递
- *表传递的数据类型为列表
- **表传递的数据类型为字典
- 位置参数,必须一一对应,缺一不行,多一也不行
- 关键字参数,无需一一对应,缺一不行,多一也不行
- 关键字参数和位置参数混合使用时,位置参数必须在关键字参数左边
#将上述程序进行修改
import time
def timmer(func):
def wrapper(*args,**kwargw):
start_time = time.time()
func(*args,**kwargw)
stop_time = time.time()
print('程序运行时间是%s' % (stop_time - start_time))
return wrapper
@timmer
def test1(name,age):
time.sleep(3)
print('test程序运行完毕,名字是%s,年龄是%s' %(name,age))
@timmer
def test2(name,age,gender):
time.sleep(3)
print('test程序运行完毕,名字是%s,年龄是%s,性别是%s' %(name,age,gender))
test1('xhg',age = 18)
test2('xhg', 18, gender = 'male') #执行结果
test程序运行完毕,名字是xhg,年龄是18
程序运行时间是3.000171422958374
test程序运行完毕,名字是xhg,年龄是18,性别是male
程序运行时间是3.000171422958374
#写在后面
时间很快,2018年很快结束
研究生的日子,过得那么单调乏味
感觉还是本科那会逍遥自在
我是一个很少追剧 追星 追综艺的人
感觉不像这个时代的
但奇葩说是我一直坚持看的
不管别人怎么评价这个节目 反正我很喜欢
他会让我思考 让我从不同的角度去看待这个世界
在辩论死忙时间这一期节目
我觉得特别精彩
我泪点好像有点低 也可能有些话确实触及到我内心深处
虫仔生病的那段经历讲述 让我看到了每一个人的不容易
每一个成年人都有自己的秘密 都生活的那么不容易 每个人都在扛着 没有放弃 每个人也都扛着很好
每一个辩手都有自己的故事
每一个普通人也有自己的故事
关键是 看你以什么样的心态去看待
我特别喜欢黄执中,他说,美好的事物,不是没有裂痕,而是满是裂痕,却没有崩开
通过别人的故事,来更加清楚豁达看待自己的生活
加油,小伙郭
加油,每一个在努力的人
当你觉得很累的时候,躺下来好好休息一下。因为一切都会好起来的!努力的人,运气都不会太差
Python小白学习之路(二十四)—【装饰器】的更多相关文章
- Python小白学习之路(十四)—【作用域】【匿名函数】【编程方法论】【高阶函数】
吧啦吧啦内心戏 在没有具体学作用域之前,我在之前的学习笔记中就有提到 我开始以为是自己自创的词儿 没想到这个词早已经存在(手动捂脸) 真是个无知的小火锅(不知者无罪) 我发现自己最擅长做的事情,就是给 ...
- Python小白学习之路(十六)—【内置函数一】
将68个内置函数按照其功能分为了10类,分别是: 数学运算(7个) abs() divmod() max() min() pow() round() sum() 类型转换(24个) bo ...
- Python小白学习之路(十二)—【前向引用】【风湿理论】
前向引用 风湿理论(函数即变量) 理论总是很抽象,我个人理解: 代码从上到下执行,一旦遇到定义的函数体,内存便为其开辟空间,并用该函数的名字作为一个标识但是该函数体内具体是什么内容,这个时候并不着急去 ...
- Python小白学习之路(十)—【函数】【函数返回值】【函数参数】
写在前面: 昨天早睡之后,感觉今天已经恢复了百分之八十的样子 又是活力满满的小伙郭 今日份鸡汤: 我始终相信,在这个世界上,一定有另一个自己,在做着我不敢做的事,在过着我想过的生活.-------宫崎 ...
- Python小白学习之路(十八)—【内置函数三】
一.对象操作 help() 功能:返回目标对象的帮助信息 举例: print(help(input)) #执行结果 Help on built-in function input in module ...
- Python小白学习之路(十五)—【map()函数】【filter()函数】【reduce()函数】
一.map()函数 map()是 Python 内置的高阶函数 有两个参数,第一个是接收一个函数 f(匿名函数或者自定义函数都OK啦):第二个参数是一个 可迭代对象 功能是通过把函数 f 依次作用在 ...
- Python小白学习之路(十九)—【文件操作步骤】【文件操作模式】
一.文件操作步骤 step1:打开文件,得到文件句柄并赋值给一个变量step2:通过句柄对文件进行操作step3:关闭文件 举例: a = open('hello world', 'r', encod ...
- 嵌入式Linux驱动学习之路(二十四)Nor Flash驱动程序
Nor Flash和Nand Flash的不同: 类型 NOR Flash Nand Flash 接口 RAM-like,引脚多 引脚少 容量 小(1M.2M...) 大(512M.1G) 读 简 ...
- IOS学习之路二十四(UIImageView 加载gif图片)
UIImageView 怎样加载一个gif图片我还不知道(会的大神请指教),不过可以通过加载不同的图片实现gif效果 代码如下: UIImageView* animatedImageView = [[ ...
- IOS学习之路二十四(custom 漂亮的UIColor)
下面简单列举一下漂亮的和颜色,大家也可以自己依次试一试选出自己喜欢的. 转载请注明 本文转自:http://blog.csdn.net/wildcatlele/article/details/1235 ...
随机推荐
- 谷歌开源OCR,tesseract-ocr使用笔记
官方教程地址:https://github.com/tesseract-ocr/tesseract/wiki/Compiling 测试版本为 root@9a2a063f9534:/tesseract/ ...
- oracle创建表空间、添加数据库文件
创建表空间: create [undo|TEMPORARY]tablespace venn datafile '/opt/oracle/db01/app/oracle/oradata/OSSORCL/ ...
- myeclipse部署项目的时候报No projects are available for deployment to this server但是项目明明存在
如题,今天在尝试部署从SVN上down下来的项目时,发现不能被tomcat识别成web项目!原因是SVN上down下来的项目的结构并非典型的web项目. 解决办法,右键项目->properti ...
- Python脚本语言第一行的写法
脚本语言的第一行,目的就是指出,你想要你的这个文件中的代码用什么可执行程序去运行它,就这么简单 #!/usr/bin/python是告诉操作系统执行这个脚本的时候,调用/usr/bin下的python ...
- 2019.01.24 bzoj3125: CITY(轮廓线dp)
传送门 题意简述:给一个n∗mn*mn∗m的网格图,有的格子不能走,有的格子只能竖着走,有的格子只能横着走,问用一条回路覆盖所有能走的格子的方案数. 思路: 就是简单的轮廓线dpdpdp加了一点限制而 ...
- 2019.01.01 bzoj3625:小朋友和二叉树(生成函数+多项式求逆+多项式开方)
传送门 codeforces传送门codeforces传送门codeforces传送门 生成函数好题. 卡场差评至今未过 题意简述:nnn个点的二叉树,每个点的权值KaTeX parse error: ...
- Win7 VS2015及MinGW环境编译矢量库agg-2.5和cairo-1.14.6
书接上文,昨天装了MinGW,主要原因之一是要用到MSYS,所以顺手把FFMPEG又编译了一遍. 回到主题,其实我是想编译矢量库,因为最近要学习一些计算几何算法,所以找个方便的2D画图库就很重要. 说 ...
- idea关于tab的设置
新手使用,一不小心tab显示在右面了,这不学习下给搞正常点. settings===>Editor=====>Editor Tabs; Palacement设置的是tab显示的部位: Ta ...
- 第14章:MongoDB-聚合操作--聚合管道
① 聚合管道是MongoDB2.2版本引入的新功能.它由阶段(Stage)组成,文档在一个阶段处理完毕后,聚合管道会把处理结果传到下一个阶段. 每个阶段用阶段操作符(Stage Operators)定 ...
- (最短路)Silver Cow Party --POJ--3268
题目链接: http://poj.org/problem?id=3268 题意: 先求出所有牛到x的最短路,再求出x到所有牛的最短路,两者相加取最大值(单向图)(可以用迪杰斯特拉,SPFA) 迪杰斯特 ...