目录

* 无参装饰器(掌握) <https://www.cnblogs.com/nickchen121/p/10771174.html#无参装饰器掌握>
* 什么是装饰器? <https://www.cnblogs.com/nickchen121/p/10771174.html#什么是装饰器>
* 为什么要用装饰器? <https://www.cnblogs.com/nickchen121/p/10771174.html#为什么要用装饰器>
* 怎么用装饰器? <https://www.cnblogs.com/nickchen121/p/10771174.html#怎么用装饰器>
* 完善装饰器 <https://www.cnblogs.com/nickchen121/p/10771174.html#完善装饰器>
* 装饰器语法糖 <https://www.cnblogs.com/nickchen121/p/10771174.html#装饰器语法糖>
* 装饰器模板 <https://www.cnblogs.com/nickchen121/p/10771174.html#装饰器模板>
* 有参装饰器(掌握) <https://www.cnblogs.com/nickchen121/p/10771174.html#有参装饰器掌握>
* 三层闭包 <https://www.cnblogs.com/nickchen121/p/10771174.html#三层闭包>
无参装饰器(掌握)

什么是装饰器?


器指的是工具,而程序中的函数就是具备某一功能的工具,所以装饰器指的是为被装饰器对象添加额外功能。因此定义装饰器就是定义一个函数,只不过该函数的功能是用来为其他函数添加额外的功能。

需要注意的是:

* 装饰器本身其实是可以任意可调用的对象
* 被装饰的对象也可以是任意可调用的对象
为什么要用装饰器?


如果我们已经上线了一个项目,我们需要修改某一个方法,但是我们不想修改方法的使用方法,这个时候可以使用装饰器。因为软件的维护应该遵循开放封闭原则,即软件一旦上线运行后,软件的维护对修改源代码是封闭的,对扩展功能指的是开放的。

装饰器的实现必须遵循两大原则:

* 不修改被装饰对象的源代码
* 不修改被装饰对象的调用方式
装饰器其实就是在遵循以上两个原则的前提下为被装饰对象添加新功能。



怎么用装饰器?

改变源代码
import time def index(): start = time.time() print('welcome to index')
time.sleep(1) end = time.time() print(F"index run time is {start-end}") index()
welcome to index index run time is -1.000546932220459
编写重复代码
import time def index(): print('welcome to index') time.sleep(1) def f2():
print('welcome to index') time.sleep(1) start = time.time() index() end =
time.time() print(F"index run time is {start-end}") start = time.time() f2()
end = time.time() print(F"f2 run time is {start-end}") welcome to index index
run time is -1.0008552074432373 welcome to index f2 run time is
-1.000605821609497
第一种传参方式:改变调用方式
import time def index(): print('welcome to index') time.sleep(1) def
time_count(func): start = time.time() func() stop = time.time() print(f"{func}
time is {start-end}") time_count(index) welcome to index <function index at
0x10471cd90> time is 0.013829231262207031
第二种传参方式:包给函数-外包
import time def index(): print('welcome to index') time.sleep(1) def
time_count(func): # func = 最原始的index def wrapper(): start = time.time() func()
stop = time.time() print(f"{func} time is {start-end}") return wrapper # f =
time_count(index) # f() index = time_count(index) # index为被装饰函数的内存地址,即index =
wrapper index() # wrapper() welcome to index <function index at 0x104629ea0>
time is 1.028921127319336
完善装饰器


上述的装饰器,最后调用index()的时候,其实是在调用wrapper(),因此如果原始的index()有返回值的时候,wrapper()函数的返回值应该和index()的返回值相同,也就是说,我们需要同步原始的index()和wrapper()方法的返回值。
import time def index(): print('welcome to index') time.sleep(1) return 123
def time_count(func): # func = 最原始的index def wrapper(): start = time.time() res
= func() stop = time.time() print(f"{func} time is {start-end}") return res
return wrapper index = time_count(index) res = index() print(f"res: {res}")
welcome to index <function index at 0x102472048> time is 2.0456860065460205
res: 123
如果原始的index()方法需要传参,那么我们之前的装饰器是无法实现该功能的,由于有wrapper()=index(),所以给wrapper()方法传参即可。
import time def index(): print('welcome to index') time.sleep(1) return 123
def home(name): print(f"welcome {name} to home page") time.sleep(1) return name
def time_count(func): # func = 最原始的index def wrapper(*args, **kwargs): start =
time.time() res = func(*args, **kwargs) stop = time.time() print(f"{func} time
is {start-end}") return res return wrapper home = time_count(home) res =
home('egon') print(f"res: {res}") welcome egon to home page <function home at
0x10471cd90> time is 3.0677449703216553 res: egon
装饰器语法糖

在被装饰函数正上方,并且是单独一行写上@装饰器名


import time def time_count(func): # func = 最原始的index def wrapper(*args,
**kwargs): start = time.time() res = func(*args, **kwargs) stop = time.time()
print(f"{func} time is {start-end}") return res return wrapper @time_count #
home = time_count(home) def home(name): print(f"welcome {name} to home page")
time.sleep(1) return name @time_count # index = time_count(index) def index():
print('welcome to index') time.sleep(1) return 123 res = home('egon')
print(f"res: {res}") welcome egon to home page <function home at 0x102472048>
time is 4.091389894485474 res: egon
装饰器模板
def deco(func): def wrapper(*args,**kwargs): res = func(*args,**kwargs) return
res return wrapper
有参装饰器(掌握)

无参装饰器只套了两层,本节将讲一个套三层的装饰器——有参装饰器,但现在我们先实现一个用户登录注册的装饰器。
import time current_user = {'username': None} def login(func): # func =
最原始的index def wrapper(*args, **kwargs): if current_user['username']: res =
func(*args, **kwargs) return res user = input('username: ').strip() pwd =
input('password: ').strip() if user == 'nick' and pwd == '123': print('login
successful') current_uesr['usre'] = user res = func(*args, **kwargs) return res
else: print('user or password error') return wrapper @login def home(name):
print(f"welcome {name} to home page") time.sleep(1) return name @login def
index(): print('welcome to index') time.sleep(1) return 123 res = index()
username: nick password: 123 login successful welcome to index
对于上面的登录注册,我们把用户登录成功的信息写入内存当中。但是在工业上,用户信息可以存在文本中、mysql中、mongodb当中,但是我们只让用户信息来自于
file的用户可以认证。因此我们可以改写上述的装饰器。


import time current_user = {'username': None} def login(func): # func =
最原始的index def wrapper(*args, **kwargs): if current_user['username']: res =
func(*args, **kwargs) return res user = input('username: ').strip() pwd =
input('password: ').strip() engine = 'file' if engine == 'file': print('base of
file') if user == 'nick' and pwd == '123': print('login successful')
current_uesr['usre'] = user res = func(*args, **kwargs) return res else:
print('user or password error') elif engine == 'mysql': print('base of mysql')
elif engine == 'mongodb': print('base of mongodb') else: print('default')
return wrapper @login def home(name): print(f"welcome {name} to home page")
time.sleep(1) @login def index(): print('welcome to index') time.sleep(1) res =
index() username: nick password: 123 base of file login successful welcome to
index
三层闭包

我们首先看看闭包,包三层怎么运用。


def f1(y): def f2(): x = 1 def f3(): print(f"x: {x}") print(f"y: {y}") return
f3 return f2 f2 = f1(2) f3 = f2() f3() x: 1 y: 2
现在需求改了,我们需要判断用户动态的获取用户密码的方式,如果是file类型的,我们则让用户进行认证。因此我们可以使用有参装饰器。
import time current_uesr = {'username': None} def auth(engine='file'): def
login(func): # func = 最原始的index def wrapper(*args, **kwargs): if
current_user['username']: res = func(*args, **kwargs) return res user =
input('username: ').strip() pwd = input('password: ').strip() if engine ==
'file': print('base of file') if user == 'nick' and pwd == '123': print('login
successful') current_uesr['usre'] = user res = func(*args, **kwargs) return res
else: print('user or password error') elif engine == 'mysql': print('base of
mysql, please base of file') elif engine == 'mongodb': print('base of mongodb,
please base of file') else: print('please base of file') return wrapper return
login @auth(engine='mysql') def home(name): print(f"welcome {name} to home
page") time.sleep(1) @auth(engine='file') def index(): print('welcome to
index') time.sleep(1) res = index() username: nick password: 123 base of file
login successful welcome to index
由于两层的装饰器,参数必须得固定位func
,但是三层的装饰器解除了这个限制。我们不仅仅可以使用上述单个参数的三层装饰器,多个参数的只需要在三层装饰器中多加入几个参数即可。也就是说装饰器三层即可,多加一层反倒无用。


友情链接
ioDraw流程图
API参考文档
OK工具箱
云服务器优惠
阿里云优惠券
腾讯云优惠券
华为云优惠券
站点信息
问题反馈
邮箱:ixiaoyang8@qq.com
QQ群:637538335
关注微信