【函数的有用信息】

例:
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. Java8常用新特性实践

    前言: 时下Oracle开速迭代的Java社区以即将推出Java10,但尴尬的是不少小中企业仍使用JDK7甚至JDK6开发. 从上面列出的JDK8特性中我们可以发现Java8的部分特性很明显的是从Sc ...

  2. SQLAlchemy使用说明之ORM

    对象关系映射(Object Relation Map, ORM)可以将一个类映射为关系模式(数据表). 使用ORM比直接书写SQL在安全性,可读性上都有很大优势. Working with Relat ...

  3. [BZOJ 5072]小A的树

    Description 题库链接 给你 \(n\) 个节点的一棵树,点分黑白. \(q\) 组询问,每次询问类似于"是否存在树中 \(x\) 个点的连通块恰有 \(y\) 个黑点" ...

  4. 本地git关联远程github

    0. 前言 我们开发的项目,均在本地开发:为了保证项目进度的一致性和公开性等,我们通常将开发过程代码或成品放置到github中,本文就讲述如何使得本地git与远程github同步! PS:以下两个名称 ...

  5. python单链表

    #!/usr/bin/env python3 # -*- coding:utf-8 -*- class LNode: """ 结点类 """ ...

  6. 使用JS导出页面内容到Excel

    JS代码 <script> $(function(){ // 使用outerHTML属性获取整个table元素的HTML代码(包括<table>标签),然后包装成一个完整的HT ...

  7. 初学nodejs之安装Express中遇到的问题: error: option `-v, --view <engine>' argument missing

    Windows安装下载nodejs地址:http://nodejs.org/download/ node -v 查看安装版本,输出版本即安装成功 之前学习了nodejs的基础,今天安装Express框 ...

  8. mac系统终端sudo免输入密码技能get

    1.需要在/etc/sudoers中配置. 这个文件的权限是r/r/n,配置之前需要加写权限. sudo chmod u-w /etc/sudoers 2.打开命令窗口sudo visudo 或者 s ...

  9. C# 学习笔记(二) 时间格式化字符串

    1. 以下4种时间格式化符号输出的固定时间格式在各个区域设置中都应是相同的: 标准格式字符串 由 DateTimeFormatInfo.InvariantInfo 属性定义 自定义格式字符串 “O”或 ...

  10. 请比较throw 合throws的区别

    throw语句用在方法体内,表示抛出异常.throws语句用在方法声明的后面,表示再抛出异常,由该方法的调用者来处理.throws主要声明这个方法会抛出这种类型的异常,使它的调用者知道要捕获这个异常. ...