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 ...
随机推荐
- 2018.10.26 NOIP模拟 性感手枪(搜索)
传送门 vis[x][y]vis[x][y]vis[x][y]记录这个点是否在之前被搜过,且被搜过的坐标是什么. 然后搜索的时候记录一个循环的下标和不循环的下标就行了. 代码
- mysql学习之路_事物_存储过程_备份
数据备份与还原 备份:将当前已有的数据保留. 还原:将已经保留的数据恢复到对应表中 为什么要做数据备份 1,防止数据丢失,被盗,误操作 2,保护数据记录 数据备份还原方式有多种:数据表备份 单表数据备 ...
- linux 后台执行nohup 命令,终端断开无影响
nohup /root/start.sh & 在shell中回车后提示: [~]$ appending output to nohup.out原程序的的标准输出被自动改向到当前目录下的nohu ...
- 百度地图的js导入及使用
做页面,地图可能会用到 1 导入百度地图的js库 <script type="text/javascript" src="http://api.map.baidu. ...
- (最短路 弗洛伊德) Til the Cows Come Home -- POJ --2387
#include <iostream> #include <cstdlib> #include <cstring> #include <cstdio> ...
- kepware http接口 nodejs开发
读取某变量的值(native var http = require("http"); var options = { "method": "GET&q ...
- JavaScript常用事件参考
onabort 图像加载被中断 onblur 元素失去焦点 onchange 用户改变域的内容 onclick 鼠标点击某个对象 ondblclick 鼠标双击某个对象 onerror 当加载文档 ...
- 微信小程序-flex布局中align-items和align-self区别
首先看看菜鸟教程中关于align-items和align-self的定义 align-items:align-items 属性定义flex子项在flex容器的当前行的侧轴(纵轴)方向上的对齐方式.(对 ...
- AngularJS 脏检查机制
脏检查是AngularJS的核心机制之一,它是实现双向绑定.MVVM模式的重要基础. 一.digest循环 AngularJS将双向绑定转换为一个堆watch表达式,然后递归检查这些watch表达式的 ...
- spring security文档地址
https://docs.spring.io/spring-security/site/docs/4.1.0.RELEASE/reference/htmlsingle/