开放封闭
- 封闭:已经实现的功能代码块 不允许被修改
- 开放:但可以给这个代码块增加一些扩展
情景引入:
公司已经开发了一个登录的功能,每个人都可以登录。但由于疫情爆发,现在要对登录功能加个验证:只有不发骚的人才能登录。
装饰器写法
# ↓↓ 先来一个闭包
def set_func(func):
def call_func():
print('……验证是否发骚中……')
func()
return call_func
# @set_func
def login():
print('……排队进入中……')
login() # 输出 ……排队进入中……
当取消第8行的注释时,输出
……验证是否发骚中……
……排队进入中……
不改动函数内部的代码,利用@func
的形式,给原函数增加功能,这个func就是装饰器
装饰器实现过程
def set_func(func):
def call_func():
print('……验证是否发骚中……')
func()
return call_func
def login():
print('……排队进入中……')
# 步骤一:
ret = set_func(login)
ret()
用变量ret
指向我的闭包函数,然后ret()
执行,就完成了登录之前校验的需求。
变量名可以随意取,那么把ret
叫做login
也没问题:
# 步骤2——>因为ret可以随意起名
login = set_func(login)
login()
写法简化:
# 步骤3——>写法简化
@set_func # 等价于 login = set_fun(login)
def login():
print('……排队进入中……')
对xx函数进行装饰
单个参数的函数
def set_func(func):
def call_func(n):
print('……验证是否发骚中……')
func(n)
return call_func
@set_func
def login(num):
print('……%d号正在排队进入中……' % num)
login(6)
# 输出:
# ……验证是否发骚中……
# ……6号正在排队进入中……
不定长参数的函数
def set_func(func):
def call_func(*args, **kwargs): # 这里的*表示告诉python这里用多个参数传递
print('……验证是否发骚中……')
func(*args, **kwargs) # 这里的*表示要进行拆包
return call_func
@set_func
def login(num, *args, **kwargs):
print('……%d号正在排队进入中……' % num)
print('……正在排队进入中……', args)
print('……正在排队进入中……', kwargs)
login(6)
login(6, 7)
login(6, 7, 8)
login(6, 7, 8, mm=10)
有返回值的函数
def set_func(func):
def call_func(*args, **kwargs):
print('……验证是否发骚中……')
return func(*args, **kwargs)
return call_func
@set_func
def login(num, *args, **kwargs):
print('……%d号正在排队进入中……' % num)
print('……正在排队进入中……', args)
print('……正在排队进入中……', kwargs)
return 'ok'
ret = login(6)
print(ret)
↑↑ 这也是通用装饰器的写法。
多个装饰器装饰同一函数
def set_func1(func):
print("……开始装饰权限1……")
def call_func(*args, **kwargs):
print('……在原功能之前新增功能1……')
func(*args, **kwargs)
print('……在原功能之后新增功能1……')
return call_func
def set_func2(func):
print("……开始装饰权限2……")
def call_func(*args, **kwargs):
print('……在原功能之前新增功能2……')
func(*args, **kwargs)
print('……在原功能之后新增功能2……')
return call_func
@set_func1
@set_func2
def login():
print('……原函数执行中……')
login()
终端结果:
……开始装饰权限2……
……开始装饰权限1……
……在原功能之前新增功能1……
……在原功能之前新增功能2……
……原函数执行中……
……在原功能之后新增功能2……
……在原功能之后新增功能1……
@set_func1
@set_func2 # 我们可以把经过func2装饰后的整体看作一个函数,然后这个新函数被func1装饰
def login():
print('……原函数执行中……')
相当于:
@set_func1
fun
这个fun相当于:
@set_func2
def login():
print('……原函数执行中……')
所以,装饰器是按照从内向外的顺序进行功能的增加的
类作为装饰器
class Test:
def __init__(self, func):
self.func = func
pass
def __call__(self, *args, **kwargs):
print('……这里是装饰器添加的功能……')
return self.func()
@Test # 相当于 get_str = Test(get_str)
def get_str():
print('hahah')
return 'haha'
get_str()
可以按照@Test相当于get_str = Test(get_str)
来倒推。
其他的,可以用@Test.xx
来做扩展。
带有参数的装饰器
def set_func(func):
def call_func():
print('……权限级别1级,验证……')
return func()
return call_func
@set_func
def test1():
print('……test1……')
return 'ok'
@set_func(1) # 带有参数的装饰器
def test2():
print('……test2……')
return 'ok'
如果装饰器后带有参数(第15行),其实是做了两步操作
- 1、调用set_func且将1当作实参传递
- 2、用上一步调用得到的返回值,当作装饰器,再对test2进行装饰
# 修改原来的装饰器:在原装饰器的外部套函数
def set_level(level_num):
def set_func(func):
def call_func():
if level_num == 1:
print('……权限级别1级,验证……')
elif level_num == 2:
print('……权限级别2级,验证……')
return func()
return call_func
return set_func
@set_level(1)
def test1():
print('……test1……')
return 'ok'
主要方法:
- 写好满足需求的普通装饰器(不带参数)
- 在原来的装饰器外层套函数,返回值是原装饰器
- 要用什么参数,就只在最外层的函数内传什么参数