裝飾器
在不改變被裝飾對象"内部代碼"以及"調用方式"的基礎上添加新的功能
開放封閉原則
對擴充開放
對修改封閉
裝飾器原則:讓使用者察覺不到程式被增加
嘗試添加裝飾器
1.原來的程式
def old(): # 建立函數
print('假裝這是代碼!') # 函數體代碼
return 123 # 傳回值
old()
假裝這是代碼!
2.新加入的功能
# 添加新的功能,不能更改原來的函數體代碼和調用方式
print('新的代碼!') # 1.直接添加新的功能
old() # 2.運作函數體代碼
print('又是一個新的代碼!') # 3.添加另一個功能
新的代碼!
假裝這是代碼!
又是一個新的代碼!
#可以運作,但如果需要多個位置需要添加同樣的功能就會繁瑣
#解決需要重複的問題
3.解決代碼需要重複的問題
def new(): # 将上一個代碼封裝成函數,可以在任意位置調用
print('新的代碼!')
old()
print('又是一個新的代碼!')
new()
新的代碼!
假裝這是代碼!
又是一個新的代碼!
# 問題,隻可以運作相同的代碼,沒有實際意義,需要靈活運用
#想辦法靈活的将新代碼添加到其他代碼之上
def old():
print('假裝這是代碼!')
return 123
def old_1(): # 1。另一個需要同樣功能的代碼
print('另一個需要新增功能的代碼!')
def new(a): # 2.函數調整為位置形參
print('新的代碼!')
a() # 4.傳入的實參直接代替a,也就是函數加上括号就會運作
print('又是一個新的代碼!')
#可以運作,但如果需要多個位置需要添加同樣的功能就會繁瑣
# 函數封裝可以在任意位置使用
new(old) # 3.調用new并将old以實參輸入,
new(old_1) # 3.1調用new并将old_1以實參輸入,
新的代碼!
假裝這是代碼!
又是一個新的代碼!
新的代碼!
另一個需要新增功能的代碼!
又是一個新的代碼!
#解決了代碼不能靈活運用的問題,但更改了原來的傳參方式
#需要解決傳參的問題
def old():
print('假裝這是代碼!')
return 123
def old_1():
print('另一個需要新增功能的代碼!')
def new_1(): # 1.建立一個閉包函數
# 但是将這個寫死了,沒辦法改變 a=old,
a = old # 2.将old直接指派給下面的a。然後運作
def new(): # 3.去掉new需要的形參
print('新的代碼!')
a() # 4.a被old指派,運作到這裡會自動找到old
print('又是一個新的代碼!')
return new # 5.new_1傳回的值為new
res = new_1() # 6.将傳回來的值指派給res
res() # 7.傳回來的new加上括号就會直接運作函數體代碼
# 更改了傳參方式,添加閉包函數
def old():
print('假裝這是代碼!')
return 123
def old_1():
print('另一個需要新增功能的代碼!')
def new_1(a): # 1.為了防止代碼不被寫死,将new_1調整為需要個實參才可以,填入的這個參數就是需要運作的old或者old_1都可以
def new():
print('新的代碼!')
a() # 2.這裡的a就是傳入的參數
print('又是一個新的代碼!')
return new
old= new_1(old) #3.關鍵
old()
# 沒有修改原代碼,沒有改變傳參方式
但如果給其他函數添加同樣的功能需要重新填寫指派等号
old_1 = new_1(old_1)
old_1()
# 那麼有沒有辦法可以在友善一些呢,或者可以添加有參函數呢
'''
關鍵:代碼運作到old=new_1(old)會先運作new_1()的函數,并将old以實參的方式傳遞進去,此時a=old的記憶體位址,new_1的傳回值時new指派old。(原函數名old記憶體位址已經被a接收,old這個變量名現在可以随時指向新的記憶體位址)然後old()是運作new的函數體代碼,old指向的就是new。運作new時a就會使用原old的記憶體位址(也就是運作old函數體代碼)
'''
裝飾器繁瑣
def old(): # 1.函數old需要參數,調用需要參數
print('假裝這是代碼!')
return 123
def old_1(b):
print('另一個需要新增功能的代碼!')
def new_1(a):
def new(*args,**kwargs): # 1.new需要位置參數或者關鍵字參數都可以運作
print('新的代碼!')
a(*args,**kwargs) # 2.傳入任何參數都可以使用或者不傳入參數
print('又是一個新的代碼!')
return new
old = new_1(old)
old()
old_1 = new_1(old_1)
old_1(1) # 3.最後需要運作的函數需要參數是以當把new傳回的時候填入參數,現在old_1就是new。運作到a的時候填入的參數會自動填入
# 完全實作了可以給無參或者有參添加新功能
但沒有傳回值
裝飾器傳回值
def old():
print('假裝這是代碼!')
return 123
def old_1(b):
print('另一個需要新增功能的代碼!')
return 456
def new_1(a):
def new(*args,**kwargs):
print('新的代碼!')
l1 = a(*args,**kwargs) # 1.将原代碼的傳回值設定變量名,接受傳回值
print('又是一個新的代碼!')
return l1 #2.将傳回值傳回出去
return new
old = new_1(old)
l2=old() # 3.将傳回值接收
old_1 = new_1(old_1)
l3=old_1(1) # 3.1将傳回值接收
print(l2) #列印
print(l3) #列印
登入認證
def add():
print('這是111')
def app():
print('這是222')
def xpp(a): #形參不可以更改
def all(*args,**kwargs): #形參不可以更改
name = input('賬号').strip()
pasd = input('密碼').strip()
if name == 'x'and pasd == '123':
i1 = a(*args,**kwargs)
return i1
else:
print('輸入錯誤!')
return
return all
add = xpp(add)
i2=add()
print()
一次性登入認證
def add():
print('這是111')
def app():
print('這是222')
dict_1 = {'1':False} # 類似功能字典
def xpp(a):
def all(*args,**kwargs):
if dict_1.get('1'): # 判斷字典内是False或者True
i1 = a(*args, **kwargs)
return i1
name = input('賬号').strip()
pasd = input('密碼').strip()
if name == 'x'and pasd == '123':
i1 = a(*args,**kwargs)
dict_1['1'] = True # 登入成功将False改成True
return i1
else:
print('輸入錯誤!')
return all
add = xpp(add)
i2=add()
print()
裝飾器模闆
def outer(func):
def inner(*args,**kwargs):
print('執行函數之前添加的功能')
res = func(*args,**kwargs) #執行被裝飾的函數
print('執行函數之後添加的功能')
return res #被裝飾的函數傳回值的傳回
return inner
def index (*args,**kwargss)
print('這是文法糖')
inder = outer(inder)
文法糖
def outer(func):
def inner(*args,**kwargs):
print('執行函數之前添加的功能')
res = func(*args,**kwargs) #執行被裝飾的函數
print('執行函數之後添加的功能')
return res #被裝飾的函數傳回值的傳回
return inner
def index (*args,**kwargss)
print('這是文法糖')
inder = outer(inder)
@outer # inder = outer(inder) 相等
裝飾器文法糖書寫規範
文法糖必須緊貼着要裝飾的對象上方
裝飾器文法糖内部原理
自動将下面緊貼着的被裝飾對象名字當做參數傳給裝飾器函數調用
雙層文法糖
import time
def get_time(func):
def inner(*args, **kwargs):
start_time = time.time()
res = func(*args, **kwargs) # 執行被裝飾的函數
end_time = time.time()
print('函數執行時間:%s'%(end_time-start_time))
return res # 将被裝飾函數執行之後的傳回值傳回
return inner
# 校驗使用者登入裝飾
def login_auth(func):
def inner(*args, **kwargs):
# 1.先擷取使用者的使用者名和密碼
username = input('username>>>:').strip()
password = input('password>>>:').strip()
# 2.校驗使用者名和密碼是否正确
if username == 'jason' and password == '123':
res = func(*args, **kwargs) # 執行被裝飾的函數
return res # 将被裝飾函數執行之後的傳回值傳回
print('使用者名或密碼錯誤 無權限執行')
return inner
@login_auth
@get_time
def index():
time.sleep(1)
print('from index')
index()
'''
先執行距離函數體最近的文法糖,之行結束的傳回值會被第二層文法糖運用
'''
裝飾器修複技術
from functools import wraps
def outer(func):
@wraps(func) # 修複技術就是為了讓被裝飾對象更加不容易被察覺裝飾了
def inner(*args, **kwargs):
print('執行函數之前可以添加的額外功能')
res = func(*args, **kwargs) # 執行被裝飾的函數
print('執行函數之後可以添加的額外功能')
return res # 将被裝飾函數執行之後的傳回值傳回
return inner
@outer # index = outer(index)
def index():
print('from index')
print(index)
help(index)
def home():
"""這是一個home函數"""
print('from home')
# help(index)
# help(home)
# print(index)
# help(len)
help() #檢視函數參數
from functools import wraps
def 函數名():
@wraps(函數名)
'''就這麼用,别問為什麼'''
練習題
def outter1(func1):
print('加載了outter1')
def wrapper1(*args, **kwargs):
print('執行了wrapper1')
res1 = func1(*args, **kwargs)
return res1
return wrapper1
def outter2(func2):
print('加載了outter2')
def wrapper2(*args, **kwargs):
print('執行了wrapper2')
res2 = func2(*args, **kwargs)
return res2
return wrapper2
def outter3(func3):
print('加載了outter3')
def wrapper3(*args, **kwargs):
print('執行了wrapper3')
res3 = func3(*args, **kwargs)
return res3
return wrapper3
@outter1
@outter2
@outter3
def index():
print('from index')
七個print的列印順序
有參裝飾器
def outer(source_data):
# source_data = 'file'
def login_auth(func):
def auth(*args,**kwargs):
# 2.校驗使用者名和密碼是否正确
# 資料的校驗方式可以切換多種
if source_data == 'file':
# 從檔案中擷取使用者資料并比對
print('file檔案擷取')
elif source_data == 'MySQL':
# 從MySQL資料庫中擷取資料比對
print('MySQL資料庫擷取')
elif source_data == 'postgreSQL':
# 從postgreSQL資料庫中擷取資料對比
print('postgreSQL資料庫擷取')
else:
print('使用者名或密碼錯誤 無法執行函數')
return auth
return login_auth
@outer('file')
def index():
print('from index')
@outer('MySQL')
def home():
print('from home')
index()
home()
#有參函數,最外層函數在包一層