一、閉包函數(shù)
閉包函數(shù)=函數(shù)嵌套定義+函數(shù)對象+名稱空間與作用域
1撬槽、閉:指的是該函數(shù)是定義在一個函數(shù)內(nèi)部的函數(shù)
2峡谊、包:值得是該函數(shù)訪問了一個來自于外層函數(shù)的變量
為函數(shù)體傳參的方法:
'''方案一:直接使用參數(shù)的形式傳遞'''
def wrapper(x):
print(x)
wrapper(111)
wrapper(222)
'''方案二:把函數(shù)體想要的參數(shù)包給它(即使用閉包的概念)'''
def outter(x):
x = 111
def wrapper(): # wrapper = 閉包函數(shù)的內(nèi)存地址
print(x)
return wrapper # 一定不要加括號
return 閉包函數(shù)的內(nèi)存地址
f1 = outter(111) # f = 閉包函數(shù)的內(nèi)存地址
f2 = outter(222) # f = 閉包函數(shù)的內(nèi)存地址
乍一看會感覺使用閉包來傳參數(shù)非常的麻煩,我們之前使用函數(shù)姓赤,需要參數(shù)都是直接傳給他赡译,方便也快捷,但是在某些場景下我們定死了某個函數(shù)無法直接傳參數(shù)不铆,那就必須通過其他方式傳參數(shù)蝌焚,即閉包的方式,下面介紹的裝飾器就是閉包的使用
二誓斥、無參裝飾器
1只洒、什么是裝飾器
器:工具
裝飾:為被裝飾者添加額外的功能
2、為何要有裝飾器
軟件一旦上線運行之后劳坑,就應(yīng)該遵循開放封閉原則:
1红碑、開放指的是對拓展新功能開放
2、封閉指的是對修改源代碼封閉
這種情況下泡垃,我們在寫新功能時析珊,若需要用到新的參數(shù),就無法直接通過原函數(shù)來傳了蔑穴,必須通過其他方式來傳參忠寻,目前我們想到的方法,是兩種傳參的另一種的方式存和,閉包函數(shù)
定義裝飾器的目的:
定義裝飾器就是為了在遵循1和2的前提下來為其他函數(shù)添加新功能的
ps:
不修改被裝飾對象指的是定義與調(diào)用都不能修改
所以下述行為都違反了開放封閉原則:
①奕剃、修改被裝飾對象定義時的源代碼
②、修改被裝飾對象的調(diào)用方式
3捐腿、如何用裝飾器:
'''無參裝飾器基本格式'''
def inner(func):
def wrapper(*args,**kwargs):
res = func(*args,**kwargs)
return func
return wrapper()
4纵朋、無參裝飾器的應(yīng)用場景以及構(gòu)建裝飾器步驟
需求:有一個index函數(shù),此時我們要在index函數(shù)基礎(chǔ)上添加一個計算運算時間的功能茄袖,因為是公司項目操软,我們不能修改原函數(shù)的調(diào)用方式,也不能修改源代碼宪祥。
一聂薪、被裝飾器對象index如下
import time
def index(x,y):
time.sleep(1)
print("index---->",x,y)
index(1,2)
二家乘、為index添加計算運算時間功能
方案1問題:修改了源代碼
import time
def index(x,y):
start = time.time()
time.sleep(1)
print("index---->",x,y)
end = time.time()
print(end-start)
index(1,2)
方案2問題:需要找到所有調(diào)用index的位置
import time
def index(x,y):
time.sleep(1)
print("index---->",x,y)
start = time.time()
index(1,2)
end = time.time()
print(end-start)
方案3問題:函數(shù)被寫死了只能算index的時間
import time
def index(x,y):
time.sleep(1)
print("index---->",x,y)
def wrapper():
start = time.time()
index(1,2)
end = time.time()
print(end-start)
wrapper()
方案4:直接傳index進去,改變了index的調(diào)用方式藏澳,現(xiàn)在需要用wrapper來調(diào)用index仁锯,我們需要再想一種傳參的方式
import time
def index(x,y):
time.sleep(1)
print("index---->",x,y)
def wrapper(func):
start = time.time()
func(1,2)
end = time.time()
print(end-start)
wrapper(index)
方案5問題:此時利用閉包函數(shù),將index包給了wrapper翔悠,再重命名為index业崖,我們在調(diào)用index時其實是調(diào)用wrapper,但有出現(xiàn)了問題,\
原函數(shù)在調(diào)用的時候需要傳參數(shù)如index(1,2)蓄愁,而wrapper接收不了參數(shù)
import time
def index(x,y):
time.sleep(1)
print("index---->",x,y)
def outter(func):
# func = index()
def wrapper():
start = time.time()
func(1,2)
end = time.time()
print(end-start)
return wrapper
index = outter(index)
index()
方案6
import time
from functools import wraps
def timmer(func):
def wrapper(*args,**kwargs):
start = time.time()
res = func(*args,**kwargs)
end = time.time()
print(end-start)
return res
return wrapper
def login(func):
def wrapper(*args,**kwargs):
name = 'yang'
pwd = '123'
inp_name = input("請輸入您的用戶名:").strip()
inp_pwd = input("請輸入您的密碼").strip()
if inp_name == name and inp_pwd == pwd:
print("登錄成功")
res = func(*args,**kwargs)
return res
return wrapper
@timmer
@login
def index(x,y):
time.sleep(1)
print("index---->",x,y)
index(1,2)
三腻要、有參裝飾器
對于不再需要新參數(shù)的裝飾器,兩層就可以解決了涝登,但是當(dāng)我們的裝飾器需要新的參數(shù)雄家,如在登錄的時候,我們要判斷我們用戶名與密碼的來源胀滚,此時需要外部將來源傳進來趟济。而兩層的裝飾器中,第一層咽笼,是為了給原函數(shù)傳參數(shù)顷编,他的參數(shù)不能動,而第二層剑刑,因為裝飾器語法@的原因媳纬,也已經(jīng)定死了此處只能傳一個函數(shù)名,也不能傳參數(shù)施掏,所以我們需要構(gòu)造第三層來接受外部的參數(shù)钮惠,而在內(nèi)部的函數(shù)可以在不改動自己依然可以獲取到外層的函數(shù).
'''有參裝飾器模版'''
def outter2(x,y,z,a,b):
def outter1(func):
def wrapper(*args,**kwargs):
res=func(*args,**kwargs)
return res
return wrapper
return outter1
def outter(engine = 'file'):
def deco2(func2):
def wrapper2(*args,**kwargs):
inp_name = input('username>>>: ').strip()
inp_pwd = input('password>>>: ').strip()
if engine == "file":
print('基于file的認證')
if inp_name == "egon" and inp_pwd == "123":
print('login successful')
res2=func2(*args,**kwargs)
return res2
else:
print('username or password error')
elif engine == 'mysql':
print('基于mysql的認證')
elif engine == 'ldap':
print('基于ldap的認證')
else:
print('未知的engine')
return wrapper2
return deco2
@outter(engine='mysql') # @deco2 # index=deco2(index)
def index(x,y):
print('index=>',x,y)
index(1,2) # index=>wrapper
四、疊加多個裝飾器的運行步驟(記住結(jié)論既可)
2.1 加載順序:自下而上
2.2 執(zhí)行順序:自上而下運行內(nèi)層的wrapper函數(shù)
結(jié)論:
1七芭、無參裝飾器的模板
def outter(func):
def wrapper(*args,**kwargs):
res=func(*args,**kwargs)
return res
return wrapper
2素挽、疊加多個裝飾器
**2.1 加載順序:自下而上**
**2.2 執(zhí)行順序:自上而下運行內(nèi)層的wrapper函數(shù)**
def deco1(func1): # func1 = wrapper2的內(nèi)存地址
def wrapper1(*args,**kwargs):
print('wrapper1====>')
res1=func1(*args,**kwargs)
return res1
return wrapper1
def deco2(func2): # func2 = wrapper3的內(nèi)存地址
def wrapper2(*args,**kwargs):
print('wrapper2====>')
res2=func2(*args,**kwargs)
return res2
return wrapper2
def deco3(func3): # func3 = 最原始的那個被裝飾函數(shù)的內(nèi)存地址
def wrapper3(*args,**kwargs):
print('wrapper3====>')
res3=func3(*args,**kwargs)
return res3
return wrapper3
# index=wrapper1的內(nèi)存地址
@deco1 # deco1(wrapper2的內(nèi)存地址)=>wrapper1的內(nèi)存地址
@deco2 # deco2(wrapper3的內(nèi)存地址)=>wrapper2的內(nèi)存地址
@deco3 # deco3(最原始的那個被裝飾函數(shù)的內(nèi)存地址)=>wrapper3的內(nèi)存地址
def index(x,y):
print('index=>',x,y)
index(1,2)
"""
wrapper1====>'
wrapper2====>
wrapper3====>
index=>1,2
"""
3 案例
import time
def deco1(func1):
def wrapper1(*args,**kwargs):
start_time = time.time()
res1=func1(*args,**kwargs)
stop_time = time.time()
print(stop_time - start_time)
return res1
return wrapper1
def deco2(func2):
def wrapper2(*args,**kwargs):
inp_name = input('username>>>: ').strip()
inp_pwd = input('password>>>: ').strip()
if inp_name == "egon" and inp_pwd == "123":
print('login successful')
res2=func2(*args,**kwargs)
return res2
else:
print('username or password error')
return wrapper2
@deco2
@deco1
def index(x,y):
time.sleep(1)
print('index=>',x,y)
index(1,2)