装饰器
灵魂三问
1.什么事装饰器
器 => 工具
装饰 => 指的是为被装饰的对象添加新功能装饰器本身可以是任意可调用的对象 => 函数
被装饰的对象也可以是任意可调用的对象 => 函数
目标:写一个函数来为另一个函数添加新功能2.为何要用装饰器
开放封闭原则:软件一旦上线就应该对修改封闭,对扩展开发
对修改封闭:
- 1.不能修改功能的源代码
- 2.也不能修改功能的调用方式
对扩展开发:
- 可以为原有的功能添加新的功能
装饰器就是要在不修改功能源代码以及调用方式的前提下为员功能添加额外新的功能3.如何用装饰器
- 如何用装饰器:
import time
def index():
print('welcome to index page')
time.sleep(2)
def outter(func): #给index功能装饰一个检测运行时间的新功能
#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() #wrapper 偷梁换柱,现在的index是wrapper的内存地址
- 装饰器修正1
如果需要index的返回值,上面如果res = index() 实际上是 res = wrapper()
import time
def index():
print('welcome to index page')
time.sleep(2)
return 123
# ===================================================
def outter (func):
#func = 最原始那个index的内存dizh
def wrapper():
start = time.time()
res = func() #最原始那个index的内存地址 把func的返回值指向res
stop = time.time()
print('run time is %s' %(stop-start))
return res #wrapper的返回值和最原始index的返回值相同
return wrapper
index = outter(index) #index = outter(最原始那个index的内存地址) #index = wrapper函数的内存地址
# ===================================================
res = index() #res = wrapper()
print(res)
- 装饰器修正2
之前被装饰的函数是无参数的函数,如果被装饰的函数是有参数的函数
import time
def index():
print('welcome to index page')
time.sleep(3)
return 123
def home(name):
print('welcome %s to home page' %name)
time.sleep(1)
# ===================================================
def outter(func):
#func = 最原始那个home的内存地址
def wrapper (*args,**kwargs): #用了之前学的*和**的用法
start = time.time()
res = func(*args,**kwargs) #如果被装饰的函数需要参数,可以把wrapper的参数传给被装饰的函数做参数
stop = time.time()
print('run time is %s' %(stop-start))
return res
return wrapper
index = outter(index) #index = outter(最原始那个index的内存地址) #index = wrapper函数的内存地址
home = outter(home) #home = outter(最原始那个home的内存地址) #home = wrapper函数的内存地址
# ===================================================
home('wood') #wrapper('wood')
index() #wrapper
- 装饰器的语法糖
之前写的装饰器最后都要把被修饰的函数名偷梁换柱给outter
index = outter(index)
home = outter(home)
如果多个函数需要用到这个装饰器,我们就要写很多的像上面那样的代码
这要写重复的东西不是很low,python怎么能low呢
这里有一个装饰器的语法糖的概念
# @装饰器的名字:要在被装饰对象正上方单独一行写上
import time
def timmer(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
@timmer #@后+装饰圈的函数名,直接使用省了index = outter(index)
def index():
'''这是index功能 '''
print('welcome to index page')
time.sleep(2)
return 123
@timmer #@后+装饰圈的函数名,直接使用省了index = home(index)
def home(name):
'''这是home功能 '''
print('welcome %s to home page' %name)
time.sleep(1)
print(help(index)) #wrapper的注释
print(index.__name__) #wrapper的名字
上面代码有一个新问题,函数有一些方法:
help可以调函数的注释
.__name__调函数名
还有很多.__的方法。
这个时候用了装饰器 index已经不是原来的index了,
现在index指的是wrapper的内存地址了,
想神不知鬼不觉的再调最原始的index方法怎么办
#from functools import wraps方法
from functools import wraps #调用这个方法
import time
def timmer(func): #写一个装饰器
@wraps(func) #在wrapper上面写一个@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
@timmer #@后+装饰圈的函数名,直接使用省了index = outter(index)
def index():
'''这是index功能 '''
print('welcome to index page')
time.sleep(2)
return 123
@timmer #@后+装饰圈的函数名,直接使用省了index = home(index)
def home(name):
'''这是home功能 '''
print('welcome %s to home page' %name)
time.sleep(1)
print(help(index)) #这就显示index的注释
print(index.__name__) #index的名字
- 无参装饰器
根据上面的装饰器可以确定一个无参装饰器的模板
#无参装饰器的模板
def outter(func):
def wrapper(*args,**kwargs):
res = func(*args,**kwargs)
return res
return wrapper
用这个写一个认证功能
import time
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 #return会结束函数
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
@auth
def index():
"""这是index功能"""
print('welcome to index page')
time.sleep(2)
return 123
@auth
def home(name):
"""这是home功能"""
print('welcome %s to home page' %name)
time.sleep(1)
- 有参装饰器
之前我们说的都是无参装饰器,装饰器本身不需要参数的,参数都是为了转给或转到原函数的,现在说有参装饰器,除了需要转给原函数的参数外,装饰器本身的功能也需要参数的情况
def outter2(xxx,yyy):
#局部内的两个闭包函数的参数都是不能动的,在需要xxx,yyy就需要在往上找了,不能找到全局作用域去吧
#再包一下,三层闭包函数,最外层的给xxx,yyy传值
def outter(func):
def wrapper(*args,**kwargs):
res = func(*args,**kwargs)
print(xxx)
print(yyy)
return res
return wrapper
return outter
我们上面的装饰器是把认证的来源写死了,如果我们是从文件中调用数据来认证,如果我们是从mysql数据库里调用数据,ldap里面调用数据
import time
user_info = {'current_user':None}
def auth2(engine = 'file'): #定义一个默认形参,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')
- 补充作用域,global与nonlocal
x = 1
def func():
x = 2
func()
print(x) #x在全局里找,x=1 1是整型不能在局部直接改
x = []
def func():
x.append(1)
x.append(2)
x.append(3)
func()
print(x) #x是列表,是可变的,可以在局部直接改
# global: 在局部声明变量是全局变量
x = 1
def func():
global x #global我就是要改
x = 2
func()
print(x) #print 2
# nonlocal:在局部声明变量是外层函数的变量,如果外层没有,就在往更外存找,知道在外层的函数
x = 333
def f1():
x = 222
def f2():
x = 111
def f3():
nonlocal x
x = 0
f3()
print('f2内部的x: ',x)
f2()
print('这是f1内部的x: ',x)
f1()
print(x)