装饰器
相关知识点
-
*args:负责将多余的位置实参汇总,赋值给args -
**kwargs:负责将多余的关键字实参汇总,赋值给kwargs -
命名空间与作用域 -
函数对象:
-
函数的嵌套定义:在函数内定义函数 -
闭包函数:父函数的返回值为一个函数,被返回的函数调用了父函数的局部变量,且该函数可以在父函数外部执行
装饰器
-
装饰器:定义一个为其他函数添加功能的函数 -
为什么要使用装饰器?
- 开放封闭原则:开放扩展功能但封闭源代码的修改
- 装饰器就是在不修改装饰对象源代码以及调用方式的前提下,为装饰对象添加新功能
-
装饰器实现
import time
def func0(x):
time.sleep(1)
print(x)
func0(0)
def func1(x):
start = time.time()
time.sleep(1)
print(x)
end = time.time()
print('方案一 运行时间:{}'.format(end - start))
func1(1)
start = time.time()
func0(2)
end = time.time()
print('方案二 运行时间:{}'.format(end - start))
def wrapper():
start = time.time()
func0(3)
end = time.time()
print('方案三 运行时间:{}'.format(end - start))
wrapper()
def wrapper(x):
start = time.time()
func0(x)
end = time.time()
print('方案四 运行时间:{}'.format(end - start))
wrapper(4)
def wrapper(*args, **kwargs):
start = time.time()
func0(*args, **kwargs)
end = time.time()
print('方案五 运行时间:{}'.format(end - start))
wrapper(5)
def outter(a):
def wrapper(*args, **kwargs):
start = time.time()
a(*args, **kwargs)
end = time.time()
print('方案六 运行时间:{}'.format(end - start))
return wrapper
f = outter(func0)
f(6)
def outter(a):
def wrapper(*args, **kwargs):
start = time.time()
a(*args, **kwargs)
end = time.time()
print('方案七 运行时间:{}'.format(end - start))
return wrapper
func0 = outter(func0)
func0(7)
运行结果:
0 1 方案一 运行时间:1.001857042312622 2 方案二 运行时间:1.0040733814239502 3 方案三 运行时间:1.0017154216766357 4 方案四 运行时间:1.007995367050171 5 方案五 运行时间:1.0145602226257324 6 方案六 运行时间:1.0046615600585938 7 方案七 运行时间:1.0094060897827148
语法糖
import time
def func0(x):
time.sleep(1)
print(x)
def outter(a):
def wrapper(*args, **kwargs):
start = time.time()
a(*args, **kwargs)
end = time.time()
print('运行时间:{}'.format(end - start))
return wrapper
func0 = outter(func0)
func0('hello')
import time
def outter(a):
def wrapper(*args, **kwargs):
start = time.time()
a(*args, **kwargs)
end = time.time()
print('运行时间:{}'.format(end - start))
return wrapper
@outter
def func0(x):
time.sleep(1)
print(x)
func0('hello')
print(func0.__name__)
运行结果 hello 运行时间:1.0050427913665771 wrapper
装饰器模板
def decorator_name(x):
def wrapper(*args, **kwargs):
x(*args, **kwargs)
return wrapper
@decorator_name
def func_name():
pass
扩展:真正实现偷梁换柱,调用者无感知
import time
def func0(x):
time.sleep(1)
print(x)
func0('hello')
print(func0.__name__)
print(help(func0))
运行结果:
hello func0 Help on function func0 in module main:
func0(x) 这是函数
None
import time
def outter(a):
def wrapper(*args, **kwargs):
'''这是装饰器'''
start = time.time()
a(*args, **kwargs)
end = time.time()
print('运行时间:{}'.format(end - start))
return wrapper
@outter
def func0(x):
'''这是函数'''
time.sleep(1)
print(x)
func0('hello')
print(func0.__name__)
print(help(func0))
运行结果:
hello 运行时间:1.011878490447998 wrapper Help on function wrapper in module main:
wrapper(*args, **kwargs) 这是装饰器
None
呕吼,露馅了
- 解决方法,将原函数的属性和方法,赋值给装饰器内的wrapper
import time
def outter(a):
def wrapper(*args, **kwargs):
'''这是装饰器'''
start = time.time()
a(*args, **kwargs)
end = time.time()
print('运行时间:{}'.format(end - start))
wrapper.__name__ = a.__name__
wrapper.__doc__ = a.__doc__
return wrapper
@outter
def func0(x):
'''这是函数'''
time.sleep(1)
print(x)
func0('hello')
print(func0.__name__)
print(help(func0))
运行结果:
hello 运行时间:1.0030155181884766 func0 Help on function func0 in module main:
func0(*args, **kwargs) 这是函数
None
但是,函数有很多属性和方法,一个一个手动修改过于麻烦,甚至可能会遗漏,但python也提供了解决方法
import time
from functools import wraps
def outter(a):
@wraps(a)
def wrapper(*args, **kwargs):
'''这是装饰器'''
start = time.time()
a(*args, **kwargs)
end = time.time()
print('运行时间:{}'.format(end - start))
return wrapper
@outter
def func0(x):
'''这是函数'''
time.sleep(1)
print(x)
func0('hello')
print(func0.__name__)
print(help(func0))
运行结果:
hello 运行时间:1.0114128589630127 func0 Help on function func0 in module main:
func0(x) 这是函数
None
有参装饰器
- 装饰器内需要传入参数,但是由于语法糖的限制,装饰器只能有一个参数,并且该参数仅用来接收被装饰对象的内存地址,如何传入其他参数?
- 解决思路:将装饰器做成闭包函数
def outter(db_type):
def auth(x):
def wrapper(*args, **kwargs):
if db_type == 'file':
name = input('请输入姓名:')
passwd = input('请输入密码:')
if name == 'zhangsan' and passwd == '123':
x(*args, **kwargs)
print('基于文件认证')
else:
print('用户名或密码错误,认证失败')
elif db_type == 'mysql':
x(*args, **kwargs)
print('基于数据库认证')
else:
print('未知认证方式,不支持')
return wrapper
return auth
auth = outter(db_type='file')
@auth
def file():
print('hello')
auth = outter(db_type='mysql')
@auth
def mysql():
print('world')
msg = input('请选择认证方式(1=file|2=mysql):').strip()
if msg =='1':
file()
elif msg == '2':
mysql()
else:
print('不支持')
def outter(db_type):
def auth(x):
def wrapper(*args, **kwargs):
if db_type == 'file':
name = input('请输入姓名:')
passwd = input('请输入密码:')
if name == 'zhangsan' and passwd == '123':
x(*args, **kwargs)
print('基于文件认证')
else:
print('用户名或密码错误,认证失败')
elif db_type == 'mysql':
x(*args, **kwargs)
print('基于数据库认证')
else:
print('未知认证方式,不支持')
return wrapper
return auth
@outter(db_type='file')
def file():
print('hello')
@outter(db_type='mysql')
def mysql():
print('world')
msg = input('请选择认证方式(1=file|2=mysql):').strip()
if msg =='1':
file()
elif msg == '2':
mysql()
else:
print('不支持')
def out_decorator_name(x,y,z):
def decorator_name(a):
def wrapper(*args, **kwargs):
a(*args, **kwargs)
return wrapper
return decorator_name
@out_decorator_name(x,y,z=1)
def func_name():
pass
多个装饰器
def timer(x1):
def wrapper1(*args, **kwargs):
x1(*args, **kwargs)
print('===>装饰器timer运行')
return wrapper1
def auth(x2):
def wrapper2(*args, **kwargs):
x2(*args, **kwargs)
print('===>装饰器auth运行')
return wrapper2
def deco(a):
def outer(x3):
def wrapper3(*args, **kwargs):
x3(*args, **kwargs)
print('===>有参装饰器运行')
return wrapper3
return outer
@timer
@auth
@deco(111)
def func():
print('===>函数运行')
print(func)
运行结果:
<function timer..wrapper1 at 0x0000017A6EFB1620>
func()
==>func() ==>wrapper1()
==>x1() ==>wrapper2()
==>x2() ==>wrapper3()
==>x3() ==>func()
==>print('===>函数运行')
==>print('===>有参装饰器运行')
==>print('===>装饰器auth运行')
==>print('===>装饰器timer运行')
===>函数运行 ===>有参装饰器运行 ===>装饰器auth运行 ===>装饰器timer运行
|