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