閉包
1.函數(shù)引用
def test1():
print("---in test1 func---")
#調用函數(shù)
test1()
#引用函數(shù)
ret=test1
print(id(ret))
print(id(test1))
#通過引用調用函數(shù)
ret()
運行結果:
---in test1 func---
123456789000
123456789000
---in test1 func---
2.什么是閉包
#定義一個函數(shù)
def test(number):
#在函數(shù)內(nèi)部在定義一個函數(shù)舆瘪,并且這個函數(shù)用到了外邊函數(shù)的變量舞吭,那么將這個函數(shù)稱為閉包
def test_in(number_in):
print("in test_in 函數(shù)俐镐,number_in is %d"%number_in)
return number+number_in
return test_in
#給test函數(shù)賦值铜幽,這個10就是給參數(shù)number
ret=test(10)
#注意這里的100其實給參數(shù)number_in
print(ret(100))
#注意這里的200其實給參數(shù)number_in
print(ret(200))
運行結果:
in test_in函數(shù)亡电,number_in is 100
110
in test_in函數(shù)髓迎,number_in is 200
210
3.重講閉包
內(nèi)部函數(shù)對外部函數(shù)作用域里變量的引用(非全局變量)則稱內(nèi)部函數(shù)為閉包。
#test2.py
def counter(start=0):
count=[start]
def incr():
count[0]+=1
return count[0]
return incr
應用test2.py
import test2
c1=test2.counter(10)
print(c1())
結果:11
print(c1())
結果:12
nonlocal訪問外部函數(shù)的局部變量
def counter(start=0):
def incr():
nonlocal start
start+=1
return start
return incr
c1=counter(5)
print(c1())
print(c1())
c2=counter(50)
print(c2())
print(c2())
print(c1())
print(c1())
print(c2())
print(c2())
4.實例
def line_conf(a,b):
def line(x):
return a*x+b
return line
line1=line_conf(1,2)
line2=line_conf(3,4)
print(line1(5))
print(line2(6))
在這個例子中,函數(shù)line與變量a腿堤,b構成閉包阀坏,在創(chuàng)建閉包的時候,我們通過line_conf的參數(shù)a,b說明了這兩個變量的取值笆檀,這樣忌堂,我們就確定了函數(shù)的最終形式(y=x+2和y=3x+4)。我們只需要變化參數(shù)a酗洒,b士修,就可以獲得不同的直線表達函數(shù),因此樱衷,我們可以看到棋嘲,閉包也具有提高代碼可復用性的作用。
如果沒有閉包矩桂,我們需要每次創(chuàng)建直線函數(shù)的時候同時說明a沸移,b,x侄榴。這樣雹锣,我們就需要傳遞更多的參數(shù)
裝飾器
1.首先理解一段代碼
def foo():
print('foo')
foo #表示是函數(shù)
foo() #表示執(zhí)行foo函數(shù)
def foo():
print('foo')
foo=lambda x: x+1
foo() #執(zhí)行下面的lambda表達式,而不是原來的foo函數(shù)
2.需求
某公司由N個業(yè)務部門和一個基礎平臺部門構成牲蜀,基礎平臺負責提供底層的功能笆制,如:數(shù)據(jù)庫操作、redis調用涣达、監(jiān)控API等功能在辆。業(yè)務部門使用基礎功能時,只需調用基礎平臺提供的功能即可度苔。如:
"""基礎平臺提供的功能如下"""
def f1():
print('f1')
def f2():
print('f2')
def f3():
print('f3')
def f4():
print('f4')
"""業(yè)務部門a調用基礎平臺提供的功能"""
f1()
f2()
f3()
f4()
"""業(yè)務部門b調用基礎平臺提供的功能"""
f1()
f2()
f3()
f4()
基礎平臺提供的功能可以被任何人使用〈衣ǎ現(xiàn)在需要對基礎平臺的所有功能進行重構,為平臺提供所有功能添加驗證機制寇窑,既要在執(zhí)行功能前進行驗證鸦概。
程序員A的做法:
"""基礎平臺提供的功能"""
def f1():
#驗證1
#驗證2
#驗證3
print('f1')
def f2():
#驗證1
#驗證2
#驗證3
print('f2')
def f3():
#驗證1
#驗證2
#驗證3
print('f3')
def f4():
#驗證1
#驗證2
#驗證3
print('f4')
"""業(yè)務部門代碼不變"""
"""A,B分別調用"""
f1()
f2()
f3()
f4()
程序員B的做法:
只對基礎平臺的代碼進行重構,其他業(yè)務部門無需做任何修改
"""基礎平臺提供的功能如下"""
def check_login():
#驗證1
#驗證2
#驗證3
pass
def f1():
check_login()
print('f1')
def f2():
check_login()
print('f2')
def f3():
check_login()
print('f3')
def f4():
check_login()
print('f4')
程序員C的做法:
寫代碼遵循開放封閉原則甩骏,雖然在這個原則是用面向對象開發(fā)窗市,但是也適用于函數(shù)式編程,簡單來說饮笛,它規(guī)定已經(jīng)實現(xiàn)的代碼不允許被修改咨察,但可以被擴展即:
- 封閉:對實現(xiàn)的功能代碼塊
- 開放:對擴展開發(fā)
若實現(xiàn)上述需求,則不允許在f1福青、f2摄狱、f3脓诡、f4內(nèi)部進行修改代碼,實現(xiàn)方案:
def w1(func):
def inner():
#驗證1
#驗證2
#驗證3
func()
return inner
@w1
def f1():
print('f1')
def f2():
print('f2')
def f3():
print('f3')
def f4():
print('f4')
執(zhí)行過程
python解釋器將代碼從上至下進行解釋:
- def w1(func):將函數(shù)w1加載到內(nèi)存媒役;
- @w1
f1=w1(f1)
表面上解釋器僅僅會解釋這兩句代碼祝谚,因為函數(shù)在沒有被調用之前其內(nèi)部代碼不會被執(zhí)行。
從表面上看解釋器會執(zhí)行這兩句酣衷,但是@w1這一句在python中是一種語法糖(@函數(shù)名)
@w1內(nèi)部會執(zhí)行以下操作
執(zhí)行w1函數(shù)
執(zhí)行w1函數(shù)交惯,并將@w1下面的函數(shù)作為w1函數(shù)的參數(shù),即:@w1等價于w1(f1)穿仪。所以商玫,內(nèi)部就會去執(zhí)行:
def inner():
#驗證1
#驗證2
#驗證3
f1() #func是參數(shù),此時func等于f1
return inner #返回的inner牡借,inner代表的是函數(shù)拳昌,非執(zhí)行函數(shù),
w1的返回值
將執(zhí)行完的2w1函數(shù)返回值賦值為@w1下面的函數(shù)f1钠龙,即將w1的返回值再重新賦值為w1炬藤,即:
新
f1=def inner():
#驗證1
#驗證2
#驗證3
原f1()
return inner
所以,以后業(yè)務部門想要執(zhí)行f1函數(shù)時碴里,就會執(zhí)行新f1函數(shù)沈矿,在新f1函數(shù)內(nèi)部先執(zhí)行驗證,在執(zhí)行原來的f1函數(shù)咬腋,然后將原來f1函數(shù)的返回值返給了業(yè)務調用者羹膳。
如此一來,即執(zhí)行了驗證功能根竿,又執(zhí)行了原來f1函數(shù)的內(nèi)容陵像,并將原f1函數(shù)返回給業(yè)務調用者。
3.再議裝飾器
#定義函數(shù):完成數(shù)據(jù)包裹
def makeBold(fn):
def wrapped():
return "<b>"+fn()+"</b>"
return wrapped
#定義函數(shù):完成數(shù)據(jù)包裹
def makeItalic(fn):
def wrapped():
return "<i>"+fn()+"</i>"
return wrapped
@makeBold
def test1():
return "hello world-1"
@makeItalic
def test2():
return "hello world-2"
@makeBold
@makeItalic
def test3():
return "hello world-3"
print(test1())
print(test2())
print(test3())
運行結果:
<b>hello world-1</b>
<i>hello world-2</i>
<b><i>hello world-3</i></b>
4.裝飾器(decorator)功能
- 引入日志
- 函數(shù)執(zhí)行時間統(tǒng)計
- 執(zhí)行函數(shù)前預備處理
- 執(zhí)行函數(shù)后清理功能
- 權限校驗等場景
- 緩存
5.示例
例1.無參數(shù)的函數(shù)
from time import ctime,sleep
def timefun(func):
def wrappedfunc():
print("%s called at %s"%(func.__name__,ctime()))
func()
return wrappedfunc
@timefun
def foo():
print("I am foo")
foo()
sleep(2)
foo()
上面代碼裝飾器執(zhí)行過程可表述為:
foo=timefun(foo)
#foo先作為參數(shù)賦值給func后寇壳,foo接收指向timefun返回的wrappedfunc
foo()
#調用foo()醒颖,即等價于調用wrappedfunc()
#內(nèi)部函數(shù)wrappedfunc被引用,所以外部函數(shù)的func變量(自由變量)并沒有釋放
#func里保存的是原foo函數(shù)對象
代碼執(zhí)行結果
例2.被裝飾的函數(shù)有參數(shù)
from time import ctime,sleep
def timefun(func):
def wrappedfunc(a,b):
print("%s called at %s"%(func.__name__,ctime()))
print(a,b)
func(a,b)
return wrappedfunc
@timefun
def foo(a,b):
print(a+b)
foo(2,8)
sleep(2)
foo(4,6)
執(zhí)行結果
例3.被裝飾的函數(shù)有不定長參數(shù)
from time import ctime,sleep
def timefun(func):
def wrappedfunc(*args,**kwargs):
print("%s called at %s"%(func.__name__,ctime()))
func(*args,**kwargs)
return wrappedfunc
@timefun
def foo(a,b,c):
print(a+b+c)
foo(1,3,5)
sleep(2)
foo(2,4,6)
執(zhí)行結果
例4壳炎,裝飾器中的return
from time import ctime,sleep
def timefun(func):
def wrappedfunc():
print("%s called at %s"%(func.__name__,ctime()))
func()
return wrappedfunc
@timefun
def foo():
print('I am foo')
@timefun
def getInfo():
return '---hello---'
foo()
sleep(2)
foo()
print(getInfo())
運行結果
如果修改裝飾器為return func泞歉,則運行結果為:
或者參考如下代碼
from time import ctime,sleep
def timefun(func):
def wrappedfunc():
print("%s called at %s"%(func.__name__,ctime()))
ret=func() #保存返回來的hello
return ret #把hello返回給調用函數(shù)
return wrappedfunc
@timefun
def foo():
print('I am foo')
@timefun
def getInfo():
return '---hello---'
foo()
sleep(2)
foo()
print(getInfo())
例5.裝飾器帶參數(shù),在原有裝飾器的基礎上匿辩,設置外部變量
from time import ctime, sleep
def timefun_arg(pre="hello"):
def timefun(func):
def wrappedfunc():
print("%s called at %s %s"%(func.__name__, ctime(), pre))
return func()
return wrappedfunc
return timefun
@timefun_arg("itcast")
def foo():
print("I am foo")
@timefun_arg("python")
def too():
print("I am too")
foo()
sleep(2)
foo()
too()
sleep(2)
too()
可以理解為:
foo()==timefun_arg("itcast")(foo)()