1、什么是装饰器
器=》工具
装饰=》指的是为被装饰对象添加新功能
装饰器本身可以是任意可调用的对象=>函数 被装饰的对象也可以是任意可调用的对象=》函数
目标:写一个函数来为另一个函数添加新功能
2、为何要用装饰器
开放封闭原则:软件一旦上线就应该对修改封闭,对扩展开放
即:对修改封闭:不能修改功能的源代码,也不能修改功能的调用方式
对扩展开放:可以为原有的功能添加新的功能
装饰器就是要在不修改功能源代码以及调用方式的前提下为原功能添加额外新的功能
3.如何用装饰器(思路步骤)******
import time
def index(): print('welcome to index page') time.sleep(3)
我们要把index函数添加一个计时功能,但不能改变源代码和调用方式
如果直接添加的话会改变源代码,所以我们要重定义一个有该功能的函数
def wrapper(): start=time.time() func() # 最原始那个index的内存地址() stop=time.time() print('run time is %s' %(stop-start))
接下来如何使用?有2种办法
1、直接加参数index 2、在外面重新定义一个函数成为闭包函数,间接取值
第一种虽然简单,但若是重复使用该函数就会变得不方便,而且也改变的原函数的调用方式
原来用index()执行,现在用index执行;第二种却有可以改善的余地,如下
def outter(func): # func=最原始那个index的内存地址 def wrapper(): start=time.time() func() # 最原始那个index的内存地址() stop=time.time() print('run time is %s' %(stop-start)) return wrapper index=outter(index) #index=outter(最原始那个index的内地址) #index=wrapper函数的内地址 index() #wraper()
将Index强行等于outter(index)偷梁换柱,使Index的等于wrapper,和原来的index不一样,但是这样
调用的方式却一样了,别人调用的时候还是index()
为了让装饰器模拟的更像,我们还要再加一些功能,比如返回值可一样、原函数的参数也能传值
我们将index的返回值设置为123
def index(): print('welcome to index page') time.sleep(3) return 123
(1)模拟原函数的返回值
当我们用装饰器修饰时候,实际用的是wrapper函数,也就是说我们需要把wrapper的返回值等于原来index的返回值。将res=func(),拿到返回值,然后在wrapper下return res ,就把返回值拿到手了
def outter(func): # func=最原始那个index的内存地址 def wrapper(): start=time.time() res=func() # 最原始那个index的内存地址() stop=time.time() print('run time is %s' %(stop-start)) return res return wrapper index=outter(index) #index=outter(最原始那个index的内地址) #index=wrapper函数的内地址 #============================================================== res=index() #res=wraper() print(res)
(2)模拟原函数的参数传值
重新设置一个带参的原函数
def home(name): print('welcome %s to home page' %name) time.sleep(1)
原函数已经被包装,home(name) 本质上是 wrapper(name),然后传给函数里面的home(name),
上一节课学到参数几种类型,其中*args和**kwargs可以组合在一起可以传任何类型的参数
def outter(func): # func=最原始那个home的内地址 def wrapper(*args,**kwargs): start=time.time() res=func(*args,**kwargs) stop=time.time() print('run time is %s' %(stop-start)) return res return wrapper
4、装饰器的语法糖
@装饰器的名字;要在被装饰对象正上方单独一行写上
通常函数里有注释信息等一系列的说明,为了让装饰器拥有原函数的信息,需要导用另一个自带的模块 可以用print(help (home)) print(home._name_)查看原函数相关信息
from functools import wraps import time def timmer(func): # func=最原始那个home的内地址 @wraps(func) def wrapper(*args,**kwargs): start=time.time() res=func(*args,**kwargs) stop=time.time() print('run time is %s' %(stop-start)) return res return wrapper
5、有参装饰器
首先无参装饰器模板:
# def outter(func): # def wrapper(*args,**kwargs): # res=func(*args,**kwargs) # return res # return wrapper
为某个函数添加认证功能,如果以前登录过了,无需输入账号密码直接执行功能
user_info={'current_user':None} 可变量 def auth(func): def wrapper(*args,**kwargs): if user_info['current_user'] is not None: res=func(*args,**kwargs) return res inp_user=input('username>>>: ').strip() inp_pwd=input('password>>>: ').strip() if inp_user == 'egon' and inp_pwd == '123': # 记录登录状态 user_info['current_user']=inp_user 站在局部却改了全局的量 print('login successful') res=func(*args,**kwargs) return res else: print('user or password error') return wrapper
补充:gilobal与nonlocal
比如
# x=1 # def func(): # x=2 # # func() # print(x) 站在全局所以先用全局的量
但如果变量是可变的话,x的量发生了改变,由此我们可以用可变量的方式在局部改变全局
# x=[] # def func(): # x.append(1) # x.append(2) # x.append(3) # # func() # print(x) x=[1,2,3]
gipbal:在局部声明变量是全局变量
# x=1 # def func(): # global x # x=2 # # func() # print(x) 2
nonlocal:在局部声明变量是外层函数的变量
x=333 def f1(): x=222 def f2(): x=111 def f3(): nonlocal x x=0 f3() print('f2内部的x: ',x) x=0 f2() print('这是f1内部的x: ',x) x=222 f1() print(x) 333
有参装饰器模板:
def outter2(xxx,yyy): def outter(func): def wrapper(*args,**kwargs): res=func(*args,**kwargs) print(xxx) print(yyy) return res return wrapper return outter
写某个函数添加认证功能,并且如果以前登录过了,无需输入账号密码直接执行,还要知道该用户基于哪个文件或数据库登录的
import time user_info={'current_user':None} def auth2(engine='file'): def auth(func): def wrapper(*args,**kwargs): if user_info['current_user'] is not None: res=func(*args,**kwargs) return res inp_user=input('username>>>: ').strip() inp_pwd=input('password>>>: ').strip() if engine == 'file': print('基于文件的认证') if inp_user == 'egon' and inp_pwd == '123': # 记录登录状态 user_info['current_user']=inp_user print('login successful') res=func(*args,**kwargs) return res else: print('user or password error') elif engine == 'mysql': print('基于mysql数据的认证') elif engine == 'ldap': print('基于ldap的认证') else: print('无法识别认证源') return wrapper return auth ------------------------------------------------------------------------------------------------------- @auth2(engine='mysql') # @auth ===> index=auth(最原始那个index的内存地址)===》index=wrapper def index(): """这是index功能""" print('welcome to index page') time.sleep(2) return 123 @auth2(engine='file') def home(name): """这是home功能""" print('welcome %s to home page' %name) time.sleep(1) index() #wrapper() home('egon')