铁乐学Python_day12_装饰器
【函数的有用信息】
例:
def login(user, pwd):
'''
功能:登录调用
参数:分别有user和pwd,作用分别是用户和密码;
return: 返回值是登录成功与否(True,False)
'''
print(user, pwd, "欢迎登录")
print(login.__name__) #查看函数名字
print(login.__doc__) #查看函数注释
login
功能:登录调用
参数:分别有user和pwd,作用分别是用户和密码;
return: 返回值是登录成功与否(True,False)
函数名.__name__ 这是查看函数名字的方法
函数名.__doc__ 这是查看函数注释的方法
上面的两个方法在做运维审计日志时很有用,配合装饰器使用可以记录下不少信息。
不过要是用在被装饰器装饰过的函数时显示的会变成是装饰器的函数名和注释,
所以需要用到wraps模块去实现显示的名字和注释仍是被装饰的原函数。
例:
from functools import wraps
# 函数工具库的warps模块来实现装饰器函数名字仍是被装饰的原函数名字
def deco(func):
@wraps(func) #加在最内层函数正上方
def wrapper(*args, **kwargs):
return func(*args, **kwargs)
return wrapper
@deco
def index():
'''首页的注释'''
print('from index')
print(index.__doc__)
print(index.__name__)
首页的注释
index
【装饰器Decorator】
装饰器本质上就是一个python函数,他可以让其他函数在不需要做任何代码变动的前提下,增加额外的功能,装饰器的返回值也是一个函数对象。
装饰器的应用场景:比如插入日志,性能测试,事务处理,缓存等等场景。
装饰器的语法糖:@紧接着装饰器的函数名,放置在需被装饰的函数上方。
因为@的形象很像一个甜甜的一个一个圈的棒棒糖,所以又被人们爱称为语法糖。
相对直接生硬的将要被装饰的函数名赋值成装饰器的函数名这种方式来说灵活和优雅得多,
就算要改动也不致于太麻烦。
例:下面是一个在调用函数时先进行打印格林威治时间的Decorator:
import time # 引入时间模块
def wrapper(func):
'''
此装饰器主要用于测试被装饰函数的工作时间
'''
def inner(*args, **kwargs):
start_time = time.time()
ret = func(*args, **kwargs)
time.sleep(0.2322) # 模拟函数执行后经过的时间
end_time = time.time()
sec = end_time - start_time
print('此程序执行时间为%s' % round(sec, 5))
return ret # 相当于是返回func()
return inner
@wrapper # 语法糖装饰上后,相当于是将下方的 hello = wrapper(hello)
def hello():
print('hero say hello')
hello()
# 相当于是执行wrapper(hello)(),就相当于是执行wrapper里层的inner函数体。
# 所以会执行额外添加的功能,同时函数也正常调用了。
# 执行效果如下:
hero say hello
此程序执行时间为0.23301
上面例子是嵌套的装饰器,它比较简单。
因为我们在内层的inner函数中有设置动态参数(*args和**kwargs)
被装饰的函数有设置参数的时候,也可以正常传递,如:
@wrapper # 语法糖装饰上后,相当于是将下方的 hello = wrapper(hello)
def hello(name):
print('hero say hello %s' % name)
hello('tiele')
# 相当于是执行wrapper(hello)(),就相当于是执行wrapper里层的inner函数体。
# 所以会执行额外添加的功能,同时函数也正常调用了。
返回:
hero say hello tiele
此程序执行时间为0.23301
但有一点要注意的
经过decorator装饰之后的函数,它们的__name__已经从原来的'函数名'变成了'inner':
print(hello.__name__)
hero say hello tiele
此程序执行时间为0.23301
inner
因为直接用查看函数名字与注释的方法去查看经过被装饰器装饰了的函数会得出的是错误的信息(返回的是装饰器的名字与注释),这不符合我们的需求,(虽然直接在inner函数体内打印func.__name__可以得出原函数名,但在全局环境下使用.__name__查看还是会出错)所以还得用上python内置的functools.wraps去支持全局查看函数名时能看到正确的原函数名:
import time # 引入时间模块
import functools # 引入函数工具模块
def wrapper(func):
'''
此装饰器主要用于测试被装饰函数的工作时间
'''
# 加在最内层函数的正上方,因为要的是最内函数的名字
@functools.wraps(func)
def inner(*args, **kwargs):
start_time = time.time()
ret = func(*args, **kwargs)
time.sleep(0.2322) # 模拟函数执行后经过的时间
end_time = time.time()
sec = end_time - start_time
print('此程序执行时间为%s' % round(sec, 5))
return ret # 相当于是返回func()
return inner
@wrapper # 语法糖装饰上后,相当于是将下方的 hello = wrapper(hello)
def hello(name):
print('hero say hello %s' % name)
hello('tiele')
# 相当于是执行wrapper(hello)(),就相当于是执行wrapper里层的inner函数体。
# 所以会执行额外添加的功能,同时函数也正常调用了。
print('函数名为'+ hello.__name__)
hero say hello tiele
此程序执行时间为0.23301
函数名为hello
如上例,看起来像是在inner函数上方也装了一个语法糖装饰器一样。
【带参数的装饰器】
函数中带参数,我们是实现了。可是,有些情况需要装饰器本身也带上参数以便控制流程和装饰器开关的,就很有必要再嵌套一层外面的函数去实现了。
例:(最外层这次参数内是动态参数,而不是接受被装饰的函数名做参数了)
from functools import wraps
# 函数工具库的warps模块来实现装饰器函数名字仍是被装饰的原函数名字
import time
# 引进时间模块
def timer(*args, **kwargs):
def wrapper(f):
@wraps(f)
# 加在最内层函数的正上方,因为要的是最内函数的名字
def inner(*args, **kwargs):
'''
此功能用于测试函数起始时间与结束时间
:param args:
:param kwargs:
:return:
'''
if flag:
StartTime = time.time()
ret = f(*args, **kwargs)
time.sleep(0.3)
EndTime = time.time()
sec = EndTime - StartTime
print('此程序执行效率%f' % sec)
else:
ret = f(*args, **kwargs)
return ret
return inner
return wrapper
flag = True
@timer()
def hello_word():
'''
向世界say hello
:return: None
'''
print('hello,word.hello,hero.')
hello_word()
print(hello_word.__name__)
print(hello_word.__doc__)
hello,word.hello,hero.
此程序执行效率0.300017
hello_word
向世界say hello
:return: None
flag为False,装饰器的计算时间功能便不会执行。
def timer(flag):
# 带flag参数,可以控制装饰器的开关
def wrapper(f):
@wraps(f)
# 加在最内层函数的正上方,因为要的是最内函数的名字
def inner(*args, **kwargs):
'''
此功能用于测试函数起始时间与结束时间
:param args:
:param kwargs:
:return:
'''
if flag:
StartTime = time.time()
ret = f(*args, **kwargs)
time.sleep(0.3)
EndTime = time.time()
sec = EndTime - StartTime
print('此程序执行效率%f' % sec)
else:
ret = f(*args, **kwargs)
return ret
return inner
return wrapper
@timer(False)
def hello_word():
'''
向世界say hello
:return: None
'''
print('hello,word.hello,hero.')
hello_word()
hello,word.hello,hero.
上面例子中,当我们将flag的值改成False,便不会触发装饰器的额外功能。
【开放封闭原则】
1.对扩展是开放的
为什么要对扩展开放呢?
任何一个程序,不可能在设计之初就已经想好了所有的功能并且未来不做任何更新和修改。
所以必须允许代码扩展、添加新功能。
2.对修改是封闭的
为什么要对修改封闭呢?
写的一个函数,很有可能已经交付给其他人使用了,如果这个时候对其进行了修改,很有可能影响其他已经在使用该函数的用户。
装饰器完美的遵循了这个开放封闭原则。
【多个装饰器装饰一个函数】
例:
def wrapper1(func):
def inner():
print('wrapper1 ,before func')
func()
print('wrapper1 ,after func')
return inner
def wrapper2(func):
def inner():
print('wrapper2 ,before func')
func()
print('wrapper2 ,after func')
return inner
@wrapper2
@wrapper1
def f():
print('in f')
f()
效果:
wrapper2 ,before func
wrapper1 ,before func
in f
wrapper1 ,after func
wrapper2 ,after func

规律就是先执行完原里函数前面的语句,再执行原函数,再到里函数后面的语句。
如图:

end
2018-4-4
铁乐学Python_day12_装饰器的更多相关文章
- 铁乐学Python_day12_作业
1.写函数,返回一个扑克牌列表,里面有52项,每一项是一个元组 例如:[('红心',2),('草花',2), -('黑桃','A')] def poker(): suit = ['红心', '梅花', ...
- Python装饰器-给你的咖啡加点料
今天你的咖啡加糖了吗? 让我们通过一个简单的例子来引出装饰器的概念及用法.在引出装饰器之前,我们先来了解一下函数的概念. 一.函数回顾 1.在python中函数是一等公民,函数也是对象.我们可以把函数 ...
- Python高手之路【四】python函数装饰器
def outer(func): def inner(): print('hello') print('hello') print('hello') r = func() print('end') p ...
- python装饰器
今天看了装饰器的一些内容,感觉@修饰符还是挺抽象的. 装饰器就是在不用改变函数实现的情况下,附加的实现一些功能,比如打印日志信息等.需要主意的是装饰器本质是一个高阶函数,她可以返回一个函数. 装饰器需 ...
- Python(四)装饰器、迭代器&生成器、re正则表达式、字符串格式化
本章内容: 装饰器 迭代器 & 生成器 re 正则表达式 字符串格式化 装饰器 装饰器是一个很著名的设计模式,经常被用于有切面需求的场景,较为经典的有插入日志.性能测试.事务处理等.装饰器是解 ...
- [原创]django+ldap实现单点登录(装饰器和缓存)
前言 参考本系列之前的文章,我们已经搭建了ldap并且可以通过django来操作ldap了,剩下的就是下游系统的接入了,现在的应用场景,我是分了2个层次,第一层次是统一认证,保证各个系统通过ldap来 ...
- PHP 装饰器模式
装饰器模式:是在不必改变原类文件和使用继承的情况下,动态地扩展一个对象的功能. [装饰器模式中主要角色] 抽象组件角色(Component):定义一个对象接口,以规范准备接受附加责任的对象,即可以给这 ...
- python cookbook 学习系列(一) python中的装饰器
简介 装饰器本质上是一个Python函数,它可以让其他函数在不需要做任何代码变动的前提下增加额外功能,装饰器的返回值也是一个函数对象.它经常用于有切面需求的场景,比如:插入日志.性能测试.事务处理.缓 ...
- python基础补漏-05-生成器和装饰器
[1]生成器 很难用简单的语言描述生成器. 生成器:从字面上来理解,就是以某种规则为基础,不断的生成数据的工具 生成器函数: 在函数中如果出现了yield关键字,那么该函数就不再是普通函数,而是生成器 ...
随机推荐
- Mahout实战---编写自己的相似度计算方法
Mahout本身提供了很多的相似度计算方法,如PCC,COS等.但是当需要验证自己想出来的相似度计算公式是否是好的,这时候需要自己实现相似度类.研究了Mahout-core-0.9.jar的源码后,自 ...
- 在Linux系统安装Nodejs最简单步骤
一.去官网下载和自己系统匹配的文件 英文网址:https://nodejs.org/en/download/ 中文网址:http://nodejs.cn/download/ 通过 uname -a ...
- spark、hadoop集群添加节点
1.首先添加hdfs的节点,将安装包上传到服务器,设置好环境变量.配置文件按之前spark集群搭建的那里进行修改. 设置完成后,要对新节点新型格式化: # hdfs dfs namenode - ...
- funny alphabet
1.A Boy Can Do Everything For Girl 2. He Is Just Kidding 3. Love Must Need Our Patience
- golang基础--func函数
函数function Go函数不支持 嵌套, 重载和默认参数 支持以下特性: 无须声明原型,不定长度长度变参,多返回值,命名返回值参数,匿名函数,闭包 定义函数使用关键字func,且左侧大括号不能另起 ...
- Linux下编译ffmpeg并用GDB调试
1.在Ubuntu界面上调处命令行界面,最方便的方式是使用快捷键Ctrl+Alt+T. 2.安装SDL SDL是一个开源的多媒体开发库,可以设置图像和视频的绘制等操作.如果不安装SDL,FFMPEG将 ...
- C# 抓取网页内容的方法
1.抓取一般内容 需要三个类:WebRequest.WebResponse.StreamReader 所需命名空间:System.Net.System.IO 核心代码: view plaincopy ...
- awk把第1列相同的第2列按“;”拼起来,最后输出第1列和拼起来的结果
[root@localhost cc]# cat 2.txt adc 3 5 a d aa 3 adfa d ba 3 adf 第1列相同的第2列按";"拼起来,最后输出第1列和拼 ...
- pageParam要求传个JSON对象的方法
pageParam要求传个JSON对象,使用方式:api.openWin({name: 'page1',url: 'page1.html',pageParam: {x: '1000',y: '2000 ...
- git 拉取远程分支报错(fatal: '' is not a commit and a branch '' cannot be created from it)
问题描述从远程git上拉取某一个分支,然后报错,拉取不了这个分支. 拉取分支的命令: git checkout -b xxx-static-19 origin/xxx-static-19 其中xxx- ...