Python全栈day20(装饰器基本理论)
一,什么是装饰器
装饰器:本质就是函数,功能是为其他函数添加附加功能
原则
1,不修改被修饰函数的源代码
2,不修改被修饰函数的调用方式
举例说明:有一个求和函数要求就算出函数的运行时间 正常代码应该这样day20-1.py
import time
def cal(l):
start_time=time.time()
res=0
for i in l:
res+=i
stop_time=time.time()
print('函数的运行时间是%s'%(stop_time-start_time))
return res res = cal(range(1,1001))
print(res) 函数的运行时间是0.0
500500
假如有多个函数都需要这种统计时间的功能,遵循开放封闭的原则不能修改函数源代码
装饰器=高阶函数+函数嵌套+闭包 (高阶函数定义 函数接受参数是一个函数名或者返回是一个函数名)
这时候就可以使用装饰器day20-2.py
import time
def timmer(func):
def wapper(*args,**kwargs):
start_time=time.time()
res=func(*args,**kwargs)
stop_time=time.time()
print('函数的运行时间是%s'%(stop_time-start_time))
return res
return wapper @timmer
def cal(l):
res=0
for i in l:
time.sleep(0.1)
res+=i
return res res = cal(range(10))
print(res) 函数的运行时间是1.0049562454223633
45
需要了解装饰器先看几个高阶函数列子,函数调用接受参数为一个函数名day20-4.py
该列子同样是增加一个统计函数运行时间的功能,修改了函数test增加了统计时间的功能,没有修改原函数foo的源代码,但是修改了调用函数的方式需要使用test(函数名)来调用,不符合装饰器规则
import time
def foo():
print('Hello World') def test(func):
#打印传递的函数的地址
print(func)
start_time=time.time()
#在内部运行函数
func()
stop_time=time.time()
#统计传递函数运行时间
print('函数运行时间是%s'%(stop_time-start_time)) test(foo)
<function foo at 0x000001C73CB79048>
Hello World
函数运行时间是0.0
优化以上代码day20-5.py
首先执行timer(foo)把函数名作为参数返回值赋值给foo 由于在函数timer里面有运行一遍函数foo然后又调用了一遍foo函数所以打印了两个from foo 不符合要求
所以高阶函数不能满足装饰器的要求
import time
def foo():
time.sleep(0.2)
print('from foo')
def timer(func):
start_time=time.time()
func()
stop_time=time.time()
print('函数运行时间是%s' % (stop_time - start_time))
return func foo=timer(foo)
foo() from foo
函数运行时间是0.2000875473022461
from foo
二,函数的闭包
简单说,闭包就是根据不同的配置信息得到不同的结果
专业的解释:闭包(Closure)是词法闭包(Lexical Closure)的简称,是引用了自由变量的函数。这个被引用的自由变量将和这个函数一同存在,即使已经离开了创造它的环境也不例外。所以,有另一种说法认为闭包是由函数和与其相关的引用环境组合而成的实体。
闭包装饰器的基本实现 day20-7.py
同样实现的功能是增加函数运行时间的统计
1,把函数名作为实参传递给装饰器函数timmer并且把返回值函数wrapper的内存地址返回给变量test
2,通过test()执行函数相当于执行了函数wrapper同时在里面执行了传递的函数test
3,在执行test()函数的前后加了代码实现统计运行时间的功能
4,函数的源代码及调用方式没有改变
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(2)
print('test函数运行完毕') test=timmer(test)
test() test函数运行完毕
函数运行时间是2.0007388591766357
可以使用python的@语法简化代码 day20-8.py 这就实现了装饰器的功能
import time
def timmer(func):
def wrapper():
start_time=time.time()
func()
stop_time=time.time()
print('函数运行时间是%s'%(stop_time-start_time))
return wrapper #@加装饰器函数名相当于执行了test=timmer(test)
@timmer
def test():
time.sleep(2)
print('test函数运行完毕') test() test函数运行完毕
函数运行时间是2.0001330375671387
以上装饰器装饰的函数test是没有返回值的这样运行是没有问题的 假如函数test有返回值需要在装饰器里面的函数wrapper里面定义返回值,否则返回为空
day20-9.py
import time
def timmer(func):
def wrapper():
start_time=time.time()
#本次就相当于运行test函数
res = func()
stop_time=time.time()
print('函数运行时间是%s'%(stop_time-start_time))
return res
return wrapper #@加装饰器函数名相当于执行了test=timmer(test)
@timmer
def test():
time.sleep(2)
print('test函数运行完毕')
return '这是函数test的返回值' #因为加了装饰器相当于已经执行了test=timmer(test)并且把函数wrapper内存返回
#给了变量test这里test加()相当于执行了函数wrapper 所以加入需要装饰的函数有返回值
#需要在函数wrapper里面定义返回值
res = test()
print(res) test函数运行完毕
函数运行时间是2.000873327255249
这是函数test的返回值
函数闭包加上参数
假如需要装饰的函数有参数怎么办,可以在装饰器里面加万能接收参数可以接收任何参数
day20-10.py
import time
def timmer(func):
#*args,**kwargs参数代表接收任何参数其中*args接收一个元祖
#**kwargs接收字典
def wrapper(*args,**kwargs):
start_time=time.time()
#本次就相当于运行test函数
res = func(*args,**kwargs)
stop_time=time.time()
print('函数运行时间是%s'%(stop_time-start_time))
return res
return wrapper #@加装饰器函数名相当于执行了test=timmer(test)
@timmer
def test(name,age):
time.sleep(2)
print('test函数运行完毕,名字是%s年龄是%s'%(name,age))
return '这是函数test的返回值' #因为加了装饰器相当于已经执行了test=timmer(test)并且把函数wrapper内存返回
#给了变量test这里test加()相当于执行了函数wrapper 所以加入需要装饰的函数有返回值
#需要在函数wrapper里面定义返回值
res = test('zhangsan',18)
print(res) test函数运行完毕,名字是zhangsan年龄是18
函数运行时间是2.0006587505340576
这是函数test的返回值
函数闭包为函数加上认证功能day20-12.py
def auth_func(func):
def wrapper(*args,**kwargs):
username=input('用户名:').strip()
passwd=input('密码:').strip()
if username == 'sb' and passwd=='123':
res = func(*args,**kwargs)
return res
else:
print('用户名或密码错误')
return wrapper @auth_func
def index():
print('欢迎来到主页') @auth_func
def home(name):
print('欢迎回家%s'%name) @auth_func
def shopping_car(name):
print('%s的购物车里有[%s,%s,%s]' %(name,'奶茶','美女','娃娃')) def order():
pass index()
home('产品经理')
shopping_car('产品经理')
最后三条语句执行对应函数,因为加了装饰器必须要先通过输入用户名和密码登录认证才能打开对应的页面
函数闭包模拟session
以上虽然实现了登录验证功能,但是每执行一个页面都需要登录一次不符合实际需要,实际只需要登录一次即可
day20-13.py
#定义一个字典包含用户名初始值为空登录状态为False
user_dic={'username':None,'login':False} def auth_func(func):
def wrapper(*args,**kwargs):
#如果已经登录了username有值并且login状态为True
#则调用函数为直接调用并且返回,因为包含return就不会
#执行以下是输入了
if user_dic['username'] and user_dic['login']:
res = func(*args,**kwargs)
return res
username=input('用户名:').strip()
passwd=input('密码:').strip()
#如果输入正确的用户名和密码则代表登录成功
#把输入的用户名赋值给字典的key username
#把登录状态login设置为True
if username == 'sb' and passwd=='123':
user_dic['username']=username
user_dic['login']=True
res = func(*args,**kwargs)
return res
else:
print('用户名或密码错误')
return wrapper @auth_func
def index():
print('欢迎来到主页') @auth_func
def home(name):
print('欢迎回家%s'%name) @auth_func
def shopping_car(name):
print('%s的购物车里有[%s,%s,%s]' %(name,'奶茶','美女','娃娃')) def order():
pass index()
home('产品经理')
shopping_car('产品经理') 用户名:sb
密码:123
欢迎来到主页
欢迎回家产品经理
产品经理的购物车里有[奶茶,美女,娃娃]
只需要在执行第一个index的时候需要输入用户名和密码,再执行home和shopping_car的时候就不需要再次输入了
以上实现了登录验证并且保存session但是把用户名和密码固定了,实际情况应该是有一个列表保留用户名和密码,等待用户输入然后去列表里面找是否对应
day20-14.py
#模拟定义一个列表,列表包含多个字典,每一个字典保存用户名和密码信息
user_list =[
{'name':'zhangsan','passwd':'123'},
{'name':'lisi','passwd':'123'},
{'name':'wangwu','passwd':'123'}
]
#定义一个字典包含用户名初始值为空登录状态为False
current_dic={'username':None,'login':False} def auth_func(func):
def wrapper(*args,**kwargs):
#如果已经登录了username有值并且login状态为True
#则调用函数为直接调用并且返回,因为包含return就不会
#执行以下的输入了
if current_dic['username'] and current_dic['login']:
res = func(*args,**kwargs)
return res
username=input('用户名:').strip()
passwd=input('密码:').strip()
for index,user_dir in enumerate(user_list):
if username == user_dir['name'] and passwd == user_dir['passwd']:
current_dic['username']=username
current_dic['login']=True
res = func(*args,**kwargs)
return res
else:
print('用户名或密码错误')
return wrapper @auth_func
def index():
print('欢迎来到主页') @auth_func
def home(name):
print('欢迎回家%s'%name) @auth_func
def shopping_car(name):
print('%s的购物车里有[%s,%s,%s]' %(name,'奶茶','美女','娃娃')) def order():
pass index()
home('产品经理')
shopping_car('产品经理')
带参数验证功能的装饰器
假如验证的方式不止一种,有可能是上面的列表的方式也有可能是数据库或者一个文件保存用户信息则需要使用带参数功能的装饰器
day20-15.py
#模拟定义一个列表,列表包含多个字典,每一个字典保存用户名和密码信息
user_list =[
{'name':'zhangsan','passwd':'123'},
{'name':'lisi','passwd':'123'},
{'name':'wangwu','passwd':'123'}
]
#定义一个字典包含用户名初始值为空登录状态为False
current_dic={'username':None,'login':False} def auth(auth_type='filedb'):
def auth_func(func):
def wrapper(*args,**kwargs):
print('认证类型是',auth_type)
#如果已经登录了username有值并且login状态为True
#则调用函数为直接调用并且返回,因为包含return就不会
#执行以下的输入了
if auth_type == 'filedb':
if current_dic['username'] and current_dic['login']:
res = func(*args,**kwargs)
return res
username=input('用户名:').strip()
passwd=input('密码:').strip()
for user_dir in user_list:
if username == user_dir['name'] and passwd == user_dir['passwd']:
current_dic['username']=username
current_dic['login']=True
res = func(*args,**kwargs)
return res
else:
print('用户名或密码错误')
elif auth_type == 'ldap':
print('不会玩ldap认证方式')
res = func(*args, **kwargs)
return res
else:
print('不知道你用的什么认证方式')
res = func(*args, **kwargs)
return res return wrapper
return auth_func @auth(auth_type='filedb')
def index():
print('欢迎来到主页') @auth(auth_type='ldap')
def home(name):
print('欢迎回家%s'%name) @auth(auth_type='sss')
def shopping_car(name):
print('%s的购物车里有[%s,%s,%s]' %(name,'奶茶','美女','娃娃')) index()
home('产品经理')
shopping_car('产品经理') 认证类型是 filedb
用户名:zhangsan
密码:123
欢迎来到主页
认证类型是 ldap
不会玩ldap认证方式
欢迎回家产品经理
认证类型是 sss
不知道你用的什么认证方式
产品经理的购物车里有[奶茶,美女,娃娃]
这里定义更深一层的装饰器函数,不同的页面采用不同的认证方式(默认认证方式是filedb)只是使用print模拟输出,并没有实际实现对应的功能
PS:使用不带参数的装饰器可以满足大部分装饰器需求,带参数的装饰器较少使用。
Python全栈day20(装饰器基本理论)的更多相关文章
- python全栈开发 * 进程池,线程理论 ,threading模块 * 180727
一.进程池 (同步 异步 返回值) 缺点: 开启进程慢 几个CPU就能同时运行几个程序 进程的个数不是无线开启的 应用: 100个任务 进程池 如果必须用多个进程 且是高计算型 没有IO型的程序 希望 ...
- Python全栈day20(解压序列)
补充:解压序列 需求一,不通过索引取一个列表的第一个元素和最后一个元素 需求二,交换两个变量的值 L=[1,2,3,4,5,6,7,8,9] #把列表第一个元素赋值给a,最后一个元素赋值给c #中间的 ...
- 战争热诚的python全栈开发之路
从学习python开始,一直是自己摸索,但是时间不等人啊,所以自己为了节省时间,决定报个班系统学习,下面整理的文章都是自己学习后,认为重要的需要弄懂的知识点,做出链接,一方面是为了自己找的话方便,一方 ...
- 老男孩Python全栈第2期+课件笔记【高清完整92天整套视频教程】
点击了解更多Python课程>>> 老男孩Python全栈第2期+课件笔记[高清完整92天整套视频教程] 课程目录 ├─day01-python 全栈开发-基础篇 │ 01 pyth ...
- python全栈学习路线
python全栈学习路线-查询笔记 查询目录 一,硬件 十一,数据 ...
- python 全栈开发之路 day1
python 全栈开发之路 day1 本节内容 计算机发展介绍 计算机硬件组成 计算机基本原理 计算机 计算机(computer)俗称电脑,是一种用于高速计算的电子计算机器,可以进行数值计算,又可 ...
- Python全栈开发
Python全栈开发 一文让你彻底明白Python装饰器原理,从此面试工作再也不怕了. 一.装饰器 装饰器可以使函数执行前和执行后分别执行其他的附加功能,这种在代码运行期间动态增加功能的方式,称之为“ ...
- Python全栈之路----目录
Module1 Python基本语法 Python全栈之路----编程基本情况介绍 Python全栈之路----常用数据类型--集合 Module2 数据类型.字符编码.文件操作 Python全栈之路 ...
- python 全栈开发,Day99(作业讲解,DRF版本,DRF分页,DRF序列化进阶)
昨日内容回顾 1. 为什么要做前后端分离? - 前后端交给不同的人来编写,职责划分明确. - API (IOS,安卓,PC,微信小程序...) - vue.js等框架编写前端时,会比之前写jQuery ...
随机推荐
- 给ECharts添加右键点击事件,实现右键功能菜单
由于项目的需要,使用ECharts 的力导向图来实现 整个EDW数据架构的血缘分析,由于ECharts并没有给组件定义有右键的事件,同时ECharts是开源的项目,所以研究了下源码,将ECharts2 ...
- 基于FPGA的PCIe接口实现(具体讲解了数据流向)
时间:2014-12-09 来源:西安电子科技大学电子工程学院 作者:姜 宁,陈建春,王 沛,石 婷 摘要 PCI Express是一种高性能互连协议,被广泛应用于网络适配.图形加速器.网络存储.大数 ...
- [转]使用jquery-webcam插件,实现人脸采集并转base64
项目需求:在ie或chrome浏览器下,调用电脑摄像头(确保使用的是笔记本电脑,或者摄像头功能正常使用的台式机),进行人脸图像采集预览,并将图片的base64码传入到后台进行后续操作.该demo适用于 ...
- android线程控制UI更新(Handler 、post()、postDelayed()、postAtTime)
依照以下的理解就是handler与ui线程有一定的关联能够由于更新界面仅仅能在主线程中全部更新界面的地方能够在接受消息的handleMessage那里还有更新界面能够在handler.port(new ...
- 进程控制函数(3)-getsid()和setsid()获取当前会话和建立新会话
pid_t setsid(void) 1.调用进程不能是进程组组长,该进程变成新会话首进程(session header) 2.该进程成为一个新进程组的组长进程. 3.需有root权限(ubuntu不 ...
- Nginx设置expires设定页面缓存时间 不缓存或一直使用缓存
配置expires expires起到控制页面缓存的作用,合理的配置expires可以减少很多服务器的请求 要配置expires,可以在http段中或者server段中或者location段中加入 l ...
- ewebeditor编辑器配合IIS6.0解析漏洞拿shell
很明显这是一个ewebeditor编辑器,这个编辑器存在可遍历目录可创建文件夹等一系列漏洞.直接在url处加../即可.若要创建文件夹直接在url后面写文件夹名称即可. 上传一张shell图片,抓包改 ...
- a标签去掉下划线
转载自:http://jingyan.baidu.com/article/a17d52853095838099c8f24e.html <a>是默认有下划线的.所以有时候为了美观,我们需要去 ...
- Setup Factory 读取安装包的配置文件
result = INIFile.GetValue(SessionVar.Expand("%SourceFolder%\\set.ini"), "set", & ...
- [转]第2台Tomcat端口变更失败解决办法
原文链接:http://blog.csdn.net/preterhuman_peak/article/details/41803361 今天在服务器上部署了两个Tomcat.于是将其中一个的serve ...