一,什么是装饰器

  装饰器:本质就是函数,功能是为其他函数添加附加功能

  原则

  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(装饰器基本理论)的更多相关文章

  1. python全栈开发 * 进程池,线程理论 ,threading模块 * 180727

    一.进程池 (同步 异步 返回值) 缺点: 开启进程慢 几个CPU就能同时运行几个程序 进程的个数不是无线开启的 应用: 100个任务 进程池 如果必须用多个进程 且是高计算型 没有IO型的程序 希望 ...

  2. Python全栈day20(解压序列)

    补充:解压序列 需求一,不通过索引取一个列表的第一个元素和最后一个元素 需求二,交换两个变量的值 L=[1,2,3,4,5,6,7,8,9] #把列表第一个元素赋值给a,最后一个元素赋值给c #中间的 ...

  3. 战争热诚的python全栈开发之路

    从学习python开始,一直是自己摸索,但是时间不等人啊,所以自己为了节省时间,决定报个班系统学习,下面整理的文章都是自己学习后,认为重要的需要弄懂的知识点,做出链接,一方面是为了自己找的话方便,一方 ...

  4. 老男孩Python全栈第2期+课件笔记【高清完整92天整套视频教程】

    点击了解更多Python课程>>> 老男孩Python全栈第2期+课件笔记[高清完整92天整套视频教程] 课程目录 ├─day01-python 全栈开发-基础篇 │ 01 pyth ...

  5. python全栈学习路线

    python全栈学习路线-查询笔记 查询目录 一,硬件                                                                    十一,数据 ...

  6. python 全栈开发之路 day1

    python 全栈开发之路 day1   本节内容 计算机发展介绍 计算机硬件组成 计算机基本原理 计算机 计算机(computer)俗称电脑,是一种用于高速计算的电子计算机器,可以进行数值计算,又可 ...

  7. Python全栈开发

    Python全栈开发 一文让你彻底明白Python装饰器原理,从此面试工作再也不怕了. 一.装饰器 装饰器可以使函数执行前和执行后分别执行其他的附加功能,这种在代码运行期间动态增加功能的方式,称之为“ ...

  8. Python全栈之路----目录

    Module1 Python基本语法 Python全栈之路----编程基本情况介绍 Python全栈之路----常用数据类型--集合 Module2 数据类型.字符编码.文件操作 Python全栈之路 ...

  9. python 全栈开发,Day99(作业讲解,DRF版本,DRF分页,DRF序列化进阶)

    昨日内容回顾 1. 为什么要做前后端分离? - 前后端交给不同的人来编写,职责划分明确. - API (IOS,安卓,PC,微信小程序...) - vue.js等框架编写前端时,会比之前写jQuery ...

随机推荐

  1. shell判断文件夹是否存在

    #shell判断文件夹是否存在 #如果文件夹不存在,创建文件夹 if [ ! -d "/myfolder" ]; then mkdir /myfolder fi #shell判断文 ...

  2. ubuntu 15.04

    修改更新源: sudo vim /etc/apt/sources.list 删除里面所有内容 方法1: 按ggdG 方法2: :%d 替换源 搜狐源: deb http://mirrors.sohu. ...

  3. VBOX Ubuntu设置与Windows的共享文件夹

    参考资料: http://jingyan.baidu.com/article/2fb0ba40541a5900f2ec5f07.html http://zycao.com/virtualbox-ubu ...

  4. 解决:Scanner输入数字总是莫名其妙多个0

    用BufferedInputStream解决 Scanner scanner = new Scanner(new BufferedInputStream(System.in)); System.out ...

  5. FreeRTOS官方翻译文档——第二章 队列管理

    2.1 概览基于 FreeRTOS 的应用程序由一组独立的任务构成——每个任务都是具有独立权限的小程序.这些独立的任务之间很可能会通过相互通信以提供有用的系统功能.FreeRTOS 中所有的通信与同步 ...

  6. jquery ajax api

    执行一个异步的HTTP(Ajax)的请求. version added: 1.5jQuery.ajax( url, [ settings ] ) url一个用来包含发送请求的URL字符串. setti ...

  7. [device]/proc/devices and /dev/

    1. /proc/devices和/dev cat /proc/devices 列出在当前运行的内核中已经注册的设备名称以及设备的Major主设备号.其中的设备信息是驱动程序在加载时生成的,也可以说是 ...

  8. PHP多进程编程(一)

    虽然PHP 中,多进程用的比较的少.但是毕竟可能是会用到了.我最近就遇到这样一个问题,用户提交几百个url以后,要读出这个url 中的标题. 当然,你不希望用户等待的太久,10s 钟应该给出个答案.但 ...

  9. HTML和CSS的盒子模型(Box model)

    本文作为属性篇的最后一篇文章, 将讲述HTML和CSS的关键—盒子模型(Box model). 理解Box model的关键便是margin和padding属性, 而正确理解这两个属性也是学习用css ...

  10. mysql --mysqli::multi_query 和 mysqli_multi_query

    语法: 对象化:bool mysqli::multi_query ( string $query ) 过程化:bool mysqli_multi_query ( mysqli $link , stri ...