【函数的有用信息】

例:
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_装饰器的更多相关文章

  1. 铁乐学Python_day12_作业

    1.写函数,返回一个扑克牌列表,里面有52项,每一项是一个元组 例如:[('红心',2),('草花',2), -('黑桃','A')] def poker(): suit = ['红心', '梅花', ...

  2. Python装饰器-给你的咖啡加点料

    今天你的咖啡加糖了吗? 让我们通过一个简单的例子来引出装饰器的概念及用法.在引出装饰器之前,我们先来了解一下函数的概念. 一.函数回顾 1.在python中函数是一等公民,函数也是对象.我们可以把函数 ...

  3. Python高手之路【四】python函数装饰器

    def outer(func): def inner(): print('hello') print('hello') print('hello') r = func() print('end') p ...

  4. python装饰器

    今天看了装饰器的一些内容,感觉@修饰符还是挺抽象的. 装饰器就是在不用改变函数实现的情况下,附加的实现一些功能,比如打印日志信息等.需要主意的是装饰器本质是一个高阶函数,她可以返回一个函数. 装饰器需 ...

  5. Python(四)装饰器、迭代器&生成器、re正则表达式、字符串格式化

    本章内容: 装饰器 迭代器 & 生成器 re 正则表达式 字符串格式化 装饰器 装饰器是一个很著名的设计模式,经常被用于有切面需求的场景,较为经典的有插入日志.性能测试.事务处理等.装饰器是解 ...

  6. [原创]django+ldap实现单点登录(装饰器和缓存)

    前言 参考本系列之前的文章,我们已经搭建了ldap并且可以通过django来操作ldap了,剩下的就是下游系统的接入了,现在的应用场景,我是分了2个层次,第一层次是统一认证,保证各个系统通过ldap来 ...

  7. PHP 装饰器模式

    装饰器模式:是在不必改变原类文件和使用继承的情况下,动态地扩展一个对象的功能. [装饰器模式中主要角色] 抽象组件角色(Component):定义一个对象接口,以规范准备接受附加责任的对象,即可以给这 ...

  8. python cookbook 学习系列(一) python中的装饰器

    简介 装饰器本质上是一个Python函数,它可以让其他函数在不需要做任何代码变动的前提下增加额外功能,装饰器的返回值也是一个函数对象.它经常用于有切面需求的场景,比如:插入日志.性能测试.事务处理.缓 ...

  9. python基础补漏-05-生成器和装饰器

    [1]生成器 很难用简单的语言描述生成器. 生成器:从字面上来理解,就是以某种规则为基础,不断的生成数据的工具 生成器函数: 在函数中如果出现了yield关键字,那么该函数就不再是普通函数,而是生成器 ...

随机推荐

  1. Nginx使用记录

    配置常见解释: ########### 每个指令必须有分号结束.################# #user administrator administrators; #配置用户或者组,默认为no ...

  2. 把AspDotNetCoreMvc程序运行在Docker上-part5:使用docker-compose

    在上一part<把AspDotNetCoreMvc程序运行在Docker上-part4:实现负载均衡>中,我们通过几个比较复杂的步骤在docker平台上实现了对网站程序的负载均衡,配置步骤 ...

  3. C# using用法

    一.using指令 使用using指令在文件顶部引入命名空间,如 using System; using System.IO; 二.using别名 用using为命名空间或类型定义别名,当引入的多个命 ...

  4. node.js 读取文件

    一般用法 var path = require("path"); var fs = require("fs"); //let filePath = path.j ...

  5. MySQL 中 You can't specify target table '表名' for update in FROM clause错误解决办法

    在MySQL中,写SQL语句的时候 ,可能会遇到You can't specify target table '表名' for update in FROM clause这样的错误,它的意思是说,不能 ...

  6. WCF DEMO2 基于HTTP-GET的元数据交换及Configure()方法以及添加MEX终结点

    using System; using System.Diagnostics; using System.Linq; using System.ServiceModel; using System.S ...

  7. Winform 常用的方法

    一,Winform 如何内嵌窗体 1,判断窗体中是否以还有内嵌窗体 private void ClosePreForm() { foreach (Control item in this.spCont ...

  8. dubbo的重试机制

    对dubbo熟悉的人对下面的配置一定不会陌生: <dubbo:reference id="xxxx" interface="xx" check=" ...

  9. 计算机网络:自顶向下方法(第七版)Wireshark实验指南

    这本书的每一章后面都提供了一个Wireshark实验,通过使用Wireshark抓包并手动对包进行分析可以帮助我们更好地理解各种协议和相关知识.然而,这个资源在网上好像很难找,我历经千辛万苦找到之后, ...

  10. hdu 1430

    魔板 Time Limit: 10000/5000 MS (Java/Others)    Memory Limit: 65536/32768 K (Java/Others)Total Submiss ...