1. 应用需求
购物车场景,有两个函数get_name和get_info,已实现
def get_name(sess):
name = sess['name']
return name
def get_info(sess):
info = sess['info']
return info
假设现在接到一个需求,需验证登录后才能获取name和info。如果我们在已有源码上直接修改,即添加if语句,这会违反开闭原则(对扩展开放,对修改关闭)。这里介绍Python开发模式中一款强大的工具——装饰器(24种开发模式就是强大)。
2. 装饰器
2.1. 不使用修饰符的方法
直接用代码实现,有
def get_name(sess):
name = sess['name']
return name
def get_info(sess):
info = sess['info']
return info
def check_for_app_1(func, sess):
is_login = sess['is_login']
if is_login:
return func(sess)
else:
return None
def check_for_app_2(func, sess):
level = sess['level']
if level > 5:
return func(sess)
else:
return None
if __name__ == '__main__':
# 接到需求1:需验证是否登录
sess = {'name': 'app_1.name', 'info': 'app_1.info', 'is_login': True}
name = check_for_app_1(get_name, sess)
print(name)
sess = {'name': 'app_1.name', 'info': 'app_1.info', 'is_login': False}
name = check_for_app_1(get_name, sess)
print(name)
# 中途改需求,接到需求2:转为需验证等级
sess = {'name': 'app_2.name', 'info': 'app_2.info', 'level': 2}
name = check_for_app_2(get_name, sess)
print(name)
sess = {'name': 'app_2.name', 'info': 'app_2.info', 'level': 10}
name = check_for_app_2(get_name, sess)
print(name)
运行结果:
app_1.name
None
None
app_2.name
2.2 使用修饰符符号@的方法
使用更加简便的写法,有
from functools import wraps
# 推荐百度@wraps学习该语句的功能
# 推荐百度*args、**kwargs学习该语句的功能
def check_for_app_1(func):
@wraps(func)
def _check(*args, **kwargs):
# print(args)
# print(type(args))
# print(type(args[0]))
sess = args[0]
if sess['is_login']:
return func(*args, **kwargs)
else:
return None
return _check
# 如果需改需求,可添加多一个check_for_app_2方法
# 同时将@check_for_app_1修改成@check_for_app_2
@check_for_app_1
def get_name(sess):
name = sess['name']
return name
@check_for_app_1
def get_info(sess):
info = sess['info']
return info
if __name__ == '__main__':
sess = {'name': 'app_1.name', 'info': 'app_1.info', 'is_login': True}
print(get_name(sess))
print(get_info(sess))
sess = {'name': 'app_1.name', 'info': 'app_1.info', 'is_login': False}
print(get_name(sess))
print(get_info(sess))
运行结果:
app_1.name
app_1.info
None
None
若get_xxx函数不带参数,有
def check_for_app_1(func):
def _check():
print('write to log.txt', func.__name__) # 记录到日志里
return func()
return _check
@check_for_app_1
def get_name():
return 'name'
@check_for_app_1
def get_info():
return 'info'
if __name__ == '__main__':
print(get_name())
print(get_info())
运行结果:
write to log.txt get_name
name
write to log.txt get_info
info
2.3 符号@的有趣用法
符号@的装饰器可以带参数,有
def check_for_app_1(log_path):
def _check_for_app_1(func):
def _check(sess):
print(log_path, end='\t')
if sess['is_login']:
return func(sess)
else:
return None
return _check
return _check_for_app_1
def check_for_app_2(func):
def _check(sess):
if sess['level'] >= 3:
return func(sess)
else:
return None
return _check
# 调用get_name方法时记录到log_name.txt日志中去
@check_for_app_1('D:/log_name.txt')
def get_name(sess):
name = sess['name']
return name
# 调用get_info方法时记录到log_app.txt日志中去
# 接到新需求,调用get_info方法时需同时验证登录和等级
@check_for_app_1('D:/log_app.txt')
@check_for_app_2
def get_info(sess):
info = sess['info']
return info
if __name__ == '__main__':
print()
sess = {'name': 'app_1.name', 'info': 'app_1.info', 'is_login': True, 'level': 5}
print(get_name(sess))
print(get_info(sess))
print()
sess = {'name': 'app_1.name', 'info': 'app_1.info', 'is_login': False, 'level': 0}
print(get_name(sess))
print(get_info(sess))
print()
sess = {'name': 'app_2.name', 'info': 'app_2.info', 'is_login': True, 'level': 0}
print(get_name(sess))
print(get_info(sess))
print()
运行结果:
D:/log_name.txt app_1.name
D:/log_app.txt app_1.info
D:/log_name.txt None
D:/log_app.txt None
D:/log_name.txt app_2.name
D:/log_app.txt None
其实上述用法等价于
def check_for_app_1(log_path):
def _check_for_app_1(func):
def _check(sess):
print(log_path, end='\t')
if sess['is_login']:
return func(sess)
else:
return None
return _check
return _check_for_app_1
def check_for_app_2(func):
def _check(sess):
if sess['level'] >= 3:
return func(sess)
else:
return None
return _check
def get_name(sess):
name = sess['name']
return name
def get_info(sess):
info = sess['info']
return info
if __name__ == '__main__':
sess = {'name': 'app_1.name', 'info': 'app_1.info', 'is_login': True, 'level': 2}
# sess = {'name': 'app_1.name', 'info': 'app_1.info', 'is_login': True, 'level': 21}
get_name_1 = check_for_app_1('E:/log_name.txt')(get_name)
print(get_name_1(sess))
get_info_1 = check_for_app_2(get_info)
get_info_1 = check_for_app_1('E:/log_info.txt')(get_info_1)
print(get_info_1(sess))
运行结果:
E:/log_name.txt app_1.name
E:/log_info.txt None