装饰器
1.什么是装饰器
是一个用来为被装饰对象添加功能的工具
被装饰者:函数
装饰器:可以用函数实现装饰器这种工具
2.为什么要用装饰器
为了在不修改被装饰对象源代码以及调用方式的前提下
为被装饰对象添加上新功能
3.如何用装饰器
无参装饰器
有参装饰器
开放封闭原则:
开放:拓展功能是开放的,不修改源代码情况下加新功能
封闭:对修改的源代码是封闭的
# 被装饰对象
import time
def foo(x):
print(f'{x})
time.sleep(3)
foo(11)
# 需求,得出上述对象打印时间
# 方案一:修改了源代码,不符合修饰器
import time
def foo(x):
start = time.time()
print(f'{x})
time.sleep(3)
print(time.time() - start)
foo(11)
foo(22)
foo(33)
# 方案二:没有修改被装饰对象的源代码与调用方式,但是有代码冗余的问题
import time
def foo(x):
print(f'{x})
time.sleep(3)
start = time.time()
foo(11)
print(time.time() - start)
start = time.time()
foo(22)
print(time.time() - start)
start = time.time()
foo(33)
print(time.time() - start)
# 方案三:定义函数能解决冗余的问题,但wrapper函数需要外部传参,而且修改了调用方式,函数内部功能不灵活。不符合装饰器
import time
def foo(x):
print(f'{x})
time.sleep(3)
def wrapper():
start = time.time
foo(11)
print(time.time = start)
wrapper()
# 方案四:形参方式,wrapper可以传参,但还是修改了调用方式,内部还是写死了。
import time
def foo(x):
print(f'{x})
time.sleep(3)
def wrapper(func):
start = time.time()
func(11)
print(time.time() - start)
wrapper(foo) # 将foo内存地址传给func
# 方案五:闭包函数传参,调用方式形式上没有改变,但内部还是死的,不能灵活传参传值
import time
def foo(x):
print(f'{x})
time.sleep(3)
def outter()
func = foo
def wrapper(func):
start = time.time()
func(11)
print(time.time() - start)
return wrapper # 不能加括号,将函数wrapper返回,进行下一步赋值操作
foo = outter(foo) # 此处的foo是一个变量,将wrapper的内存地址绑定给foo,即foo = wrapper的内存地址。形式上是foo原函数
foo()
# 方案六:闭包函数传参,源代码未改变,调用方式形式上未改变,并利用*和**写活,可以传任何值,符合装饰器
import time
def foo(x):
print(f'{x})
time.sleep(3)
def outter(func):
def wrapper(*args,**kwargs):
start = time.time()
func(*args,**kwargs):
print(time.time() - start)
return wrapper # 不能加括号
foo = outter(foo) # foo = wrapper的内存地址
print(foo) # <function outter.<locals>.wrapper at 0x00000258669C7550>
foo(11) # 等价于 wrapper(11)
# 若被修饰对象有返回值
import time
def foo(x):
print(f'{x})
time.sleep(3)
return 666
def outter(func): # func = 原foo = 地址1
def wrapper(*args,**kwargs) # wrapper = 地址2
start = time.time()
res = func(*args,**kwargs) # 去除被修饰对象的返回值赋值给res
print(time.time() - start)
return res
return wrapper # 不能加括号
foo = outter(foo) # 原foo解除地址1的绑定,新foo = outter(地址1) 原foo -> 指向地址2
res = foo(11) # 相当于 res = wrapper(11)
print res # 666
# 把wrapper伪装的跟被装饰对象一样,更进一步
import time
def foo(x):
"""
foo的提示信息
"""
print(f'{x})
time.sleep(3)
return 996
def outter(func):
def wrapper(*args,**kwargs):
start = time.time()
res = func(*args,**kwargs)
print(time.time() - start)
return res
wrapper.__doc__ = foo.__doc__ # 将wrapper的提示信息变为foo的提示信息
wrapper.__name__ = foo.__name__ # 将wrapper的名字变为foo的名字
return wrapper
foo = outter(foo)
res = foo(11)
print(res) # 996
装饰器语法糖:@
import time
from functools import wraps # 内置函数工具说明的模块
def outter(func):
@wraps(func) # 相当于帮你运行了wrapper.__name__ = func.__name__等一系列伪装
def wrapper(*args,**kwags)
start = time.time()
res = func(*args,**kwargs)
print(time.time() - start)
return res
return wrapper
@outter # "outter",名称由定义函数名决定,不是固定名称,作用就是foo = outter(foo),帮你传递了内存地址。形式 @+函数名称,写在被修饰对象的上面
def foo(x)
"""
foo 的提示信息
"""
print(f'{x}')
time.sleep(3)
return 666
res = foo(22)
print(res)
help(foo)
print(foo)
输出结果:
# 22
# 3.007246971130371
# 66
# Help on function foo in module __main__:
# foo(x)
# foo 的提示信息
# <function foo at 0x00000266A2A470D0>
无参装饰器模板
def outter(func):
def wrapper(*args,**kwargs)
res = func(*args,**kwargs)
return res
return wrapper
@outter
def foo(): # 无参
pass
foo()
叠加多个装饰器
# 例1
def deco1(func1): # func1 = wrapper2的内存地址
def wrapper1(*args, **kwargs):
print('--->运行wrapeer1')
res1 = func1(*args, **kwargs)
return res1
return wrapper1
def deco1(func2): # func2 = wrapper3的内存地址
def wrapper2(*args, **kwargs):
print('--->运行wrapeer2')
res2 = func2(*args, **kwargs)
return res2
return wrapper2
def deco3(func1): # func3 = index的内存地址
def wrapper3(*args, **kwargs):
print('--->运行wrapeer3')
res1 = func1(*args, **kwargs)
return res3
return wrapper3
"""装饰器加载自下而上,运行自上而下"""
# index = wrapper1的内存地址
@deco1 # deco1(wrapper2的内存地址) --> 返回wrapper1的内存地址
@deco2 # deco2(wrapper3的内存地址) --> 返回wrapper2的内存地址
@deco3 # deco3(index的内存地址) --> 返回wrapper3的内存地址
def index():
print('from index')
return 666
res = index() # wrapper1()
print(res)
# 例2
import time
def deco1(dunc1):
def wrapper(*args,**kwargs):
# print('Begin')
start = time.time()
res1 = func(*args,**kwargs)
print(time.timr() - start)
return res1
return wrapper1
def deco2(func2):
def wrapper(*args,**kwargs):
name = input('username>>>:').strip()
pwd = input('password>>>:').strip()
if name == 'richer' and pwd == '996'
print('Login successfully')
res2 = func2(*args,**kwargs)
return res2
else:
print('Logon failed')
return wrapper2
# func1 = wrapper1的内存地址
@deco1 # deco(wrapper2的内存地址) --> 返回wrapper1的内存地址
@deco2 # deco(index的内存地址) --> 返回wrapper2的内存地址
def index():
print('Index function')
time.sleep(3)
index()
# 输出结果:
# Begin
username>>>: richer
password>>>: 996
Login successfully
index function
16.39341425895691
# 位置交换
@deco2
@deco1
def index():
print('Index function')
time.sleep(3)
index()
# 输出结果:
username>>>: richer
password>>>: 996
Login successfully
# Begin
index function
1.0014164447784424
有参装饰器
# 其实就是叠加多层函数,最好三层。内部函数需要传参,但参数被占据,只能在外层加,*与**不能动,在不影响语法糖使用的情况下,利用有参装饰器
def outter(engine):
def deco(func):
def wrapper(*args,**kwargs)
if engine == 'file'
name = input('username>>>:').strip
pwd = input('password>>>:').strip
if name == 'richer' and pwd == '996':
print('Login successfully')
res = func(*args,**kwargs)
return res
else:
print('Login failed')
elif engine == 'mysql'
print('MySQL-based authentication')
elif engine == 'ldap'
print('ldap-based authentication')
return wrapper
return deco
@outter(engine = 'mysql') # @ 后面只能跟一个内存地址,且只能帮你传一个参数
def index():
print('index function')
# index = deco(index,engine='mysql') # 在不使用语法糖的情况下使用该种方法,可以两层函数即可,及在定义deco处加形参engine:def deco(func,engine)
index() # 相当于 wrapper()
结论:有参装饰器模板
def outter(x, y):
def deco(func):
def wrapper(*args,**args):
print(x, y)
res = func(*args,kwargs)
return res
return deco
@outter(11,22)
def index():
pass
index()