| 章節(jié)號(hào) | 內(nèi)容???????????? |
|---|---|
| 1圖片格式(png) | 寬度大于620px,保持高寬比減低為620px |
| 1-1 | 應(yīng)用 |
| 1-1-1 | 方法 |
第1章節(jié)? 閉包
-
1-1?閉包—解釋
??↓函數(shù)引用:
In [84]: def test():
...: print("1111111111111111")
...:
In [85]: test
Out[85]: <function __main__.test>
In [86]: id(test)
Out[86]: 140017952235032
In [87]: b=test
In [88]: b()
1111111111111111
??函數(shù)名就是一個(gè)指向函數(shù)塊地址的變量,這個(gè)變量就可以賦值給別的變量。
??閉包:一個(gè)函數(shù)內(nèi)部又定義一個(gè)函數(shù),而且內(nèi)部函數(shù)使用到了外部函數(shù)的變量(形式參數(shù)),則外部的函數(shù)的參數(shù)和內(nèi)部的函數(shù)統(tǒng)一構(gòu)成一個(gè)閉包。
??作用:
def test(number):
print(1)
def testin():
print(2)
print(number+100)
print(3)
return testin
test(10000)
1
3
??↑可以看到,如上的函數(shù),內(nèi)部的那個(gè)函數(shù)并未執(zhí)行。為什么呢?因?yàn)槊黠@只調(diào)用了test(),而沒有哪個(gè)地方調(diào)用了testin()。

??↑具體流程如上:
??1、python解釋器運(yùn)行到1的箭頭處,得知這里是一個(gè)函數(shù)的定義,則把這一塊代碼的首地址和test綁定。直接跳轉(zhuǎn)到2處,因?yàn)闆]有調(diào)用函數(shù),函數(shù)體是不執(zhí)行的。
??2、從2這里開始跳入函數(shù)體開始執(zhí)行。
??3、執(zhí)行箭頭3的打印語(yǔ)句。
??4、遇到了內(nèi)部的函數(shù)定義。為testin賦值。
??5、直接跳出內(nèi)部函數(shù)的定義,來(lái)到箭頭5,執(zhí)行打印語(yǔ)句。
??6、執(zhí)行返回語(yǔ)句。
??執(zhí)行到了這里,
test(10000)這一串字符,是否就代表了內(nèi)部函數(shù)的引用呢???加上一對(duì)括號(hào)()測(cè)試一下:
def test(number):
print(1)
def testin():
print(2)
print(number+100)
print(3)
return testin
test(10000)()
1
3
2
10100
??↑可以看到內(nèi)部函數(shù)已經(jīng)被調(diào)用了。
??關(guān)鍵點(diǎn)在于:
??里面的函數(shù)在調(diào)用時(shí),保存了外部函數(shù)傳入的參數(shù)。從某種程度上來(lái)說(shuō),調(diào)用內(nèi)部函數(shù)的時(shí)候,外部函數(shù)已經(jīng)執(zhí)行完畢了,這個(gè)參數(shù)應(yīng)該已經(jīng)度過(guò)了自己的生命周期,但是因?yàn)檫@是一個(gè)閉包,所以這個(gè)參數(shù)還能被內(nèi)部函數(shù)繼續(xù)使用。
def test(number):
print(1)
def testin(number1):
print(2)
print(number+100+number1)
print(3)
return testin
test(10000)(1)
??↑我們?yōu)閮?nèi)部函數(shù)再加上一個(gè)形式參數(shù),則調(diào)用時(shí)候也要作相應(yīng)改變。
1
3
2
10101
??小結(jié):外部函數(shù)的參數(shù)是基數(shù),內(nèi)部函數(shù)的參數(shù)是變數(shù)。
??特點(diǎn):外部函數(shù)的返回值,是內(nèi)部函數(shù)的一個(gè)引用。
-
1-2?閉包—應(yīng)用
def line(a,b):
def fx(x):
return a*x+b
return fx
print(line(1,4)(3))
??來(lái)看這個(gè)閉包的應(yīng)用,注意看內(nèi)部的返回值,a*x+b,這其實(shí)就是一個(gè)斜截式的直線方程,求的是y值。f(x)=a*x+b。
7
??↑來(lái)看調(diào)用后的答案。
??當(dāng)我第一次接觸閉包的應(yīng)用的時(shí)候,其實(shí)我內(nèi)心是拒絕的。我們仔細(xì)來(lái)看這個(gè)調(diào)用:
print(line(1,4)(3))
??大致,我覺得這句代碼就是,調(diào)用2次函數(shù),傳入3個(gè)參數(shù)。對(duì)不對(duì)?
??那么,這樣做從表面來(lái)看,是否是多此一舉呢?我們考慮用如下方式來(lái)達(dá)到相同目的:
def line(x,a,b):
return a*x+b
print(line(3,1,4))
7
??是不是這樣程序更清晰呢?
??但是,有這樣一個(gè)問題,讓我們從這方面來(lái)考慮:
??f(x)=a*x+b這個(gè)方程組,a和b值是常數(shù),即我們計(jì)算這個(gè)方程的時(shí)候,對(duì)于不同的x值,a和b只需要確定一次就行了,其余的時(shí)候我們只用改變x的值,就能求得不同的y值。
??考慮到這個(gè)問題,如果我們?cè)赼和b值一定的情況下,計(jì)算多個(gè)x值對(duì)應(yīng)的y,那么大概就是如下的樣子:
print(line(1,1,4))
print(line(2,1,4))
print(line(3,1,4))
print(line(4,1,4))
print(line(5,1,4))
print(line(6,1,4))
#等等
??對(duì)了,你大概注意到了,1和4,是否沒必要傳遞那么多次。有辦法解決嗎?想一想閉包。
def line(a,b):
def fx(x):
return a*x+b
return fx
fx=line(1,2)
print(fx(1))
print(fx(2))
print(fx(3))
print(fx(4))
print(fx(5))
print(fx(6))
??是不是感覺,為了一個(gè)簡(jiǎn)潔,省力,真是無(wú)所不用其極呀!
第2章節(jié)?裝飾器
-
2-1?裝飾器—工作原理
??↓首先從這段代碼起步
In [1]: def func():
...: print("func")
...:
In [2]: func
Out[2]: <function __main__.func>
In [3]: func()
func
In [7]: fun = lambda x:x+1
In [8]: fun(1)
Out[8]: 2
In [9]: fun=lambda x,y:x+5*y
In [10]: fun(1,2)
Out[10]: 11
??↓然后是這段代碼:
def func():
print("func1")
def func():
print("func2")
func()
func2
??↑python從上到下解析,上面func的地址被下面的func覆蓋了,所以執(zhí)行的是最后一個(gè)。
??下面開始需求提出及分析思路,假設(shè)類似多個(gè)函數(shù)如下:
def func1():
print("func1")
def func2():
print("func2")
def func3():
print("func4")
def func4():
print("func4")
.
.
.
??后因程序需要,每個(gè)函數(shù)需要加入一個(gè)功能N,則我們?nèi)绾谓鉀Q?顯而易見的方式為:
def func1():
print("func1")
功能N
def func2():
print("func2")
功能N
def func3():
print("func4")
功能N
def func4():
print("func4")
功能N
.
.
.
??但是如果這樣的函數(shù)有成百上千個(gè),那么一是工作量太大,二是相同的冗余代碼將大幅增多。有什么改進(jìn)方式嗎?可以考慮如下:
def function():
功能N
def func1():
print("func1")
function()
def func2():
print("func2")
function()
def func3():
print("func4")
function()
def func4():
print("func4")
function()
.
.
.
??是不是感覺突然變得很美好?
??但是,編寫代碼應(yīng)該遵循的原則是,能擴(kuò)不改,老舊不動(dòng)。在函數(shù)里加了調(diào)用,某種意義上還是修改了代碼,可能造成不可描述的問題??紤]如下改動(dòng):
def zsq(func):
print("gong neng 1 diao yong")
func()
def func1():
print("func1")
zsq(func1)
??↑寫一個(gè)新函數(shù),把功能N在新函數(shù)中實(shí)現(xiàn),這個(gè)新函數(shù)有個(gè)參數(shù),傳遞的是函數(shù)的引用,則可以在新函數(shù)中調(diào)用老函數(shù)。但是這樣有一個(gè)問題,你調(diào)用的方式發(fā)生了改變:從調(diào)用func1變成了調(diào)用zsq(func1),這有什么問題呢???就是要大量修改原來(lái)代碼中寫的func1()!?。。。。?br>
??思考解決辦法。
??核心問題在哪里,那就是我們要完成一個(gè)等式,形如:
??func1() = zsq(func1)
??如果我們?cè)趂unc1()調(diào)用之前,改變func1指向的的位置為zsq的位置,在處理好zsq接受的func1的參數(shù)問題,那不就實(shí)現(xiàn)了不修改源代碼中func1()的寫法,也實(shí)現(xiàn)了功能的改變了嗎?
??如這個(gè)樣子func1= zsq(func1)
??func1()
??有什么感覺了嗎?這是不是相當(dāng)于,調(diào)用了一個(gè)接收參數(shù)的函數(shù)(zsq(func1)),這個(gè)函數(shù)有一個(gè)返回值,并且把返回值賦值給了func1。
??那么關(guān)鍵來(lái)了,現(xiàn)在的核心問題就是:
??1、zsq這個(gè)函數(shù),必須有一個(gè)返回值,這個(gè)返回值是一個(gè)函數(shù)。
??2、zsq這個(gè)函數(shù),必須接把接受到的參數(shù)保存下來(lái),留給下一個(gè)函數(shù)調(diào)用的時(shí)候來(lái)是使用。什么函數(shù)能在執(zhí)行完畢后保存住一個(gè)傳入的變量?什么函數(shù)的返回值是一個(gè)函數(shù)??
??閉包!?。。。。。。。。。。。。。。。。。。。。。?!
#首先確定第一條:
def zsq():
pass
pass
pass
return function
#參考原函數(shù):
def zsq(func):
print("gong neng 1 diao yong")
func()
#擬改寫成:
def zsq(func):
print("gong neng 1 diao yong")
func()
return function
#根據(jù)以下代碼,確定第二條:參數(shù)要保存下來(lái),那就要函數(shù)內(nèi)定義個(gè)一個(gè)函數(shù),在內(nèi)函數(shù)中顯式寫出func,形成閉包
def zsq(func):
def XXXX():
print("gong neng 1 diao yong")
func()
return function
??↑這個(gè)內(nèi)函數(shù)應(yīng)該叫什么名字呢?我們知道,zsq(func)是要返回一個(gè)函數(shù)給func1的,然后使用func1()來(lái)調(diào)用一個(gè)函數(shù),而且這個(gè)函數(shù)必須要包含原來(lái)func1()的功能,是func1()的一個(gè)超集,那滿足這個(gè)功能的,不正是XXXX()函數(shù)么?既然我們之前寫了return function,那么XXXX就正好改為function。
def zsq(func):
def function():
print("gong neng 1 diao yong")
func()
return function
??↓完整的定義和調(diào)用如下:
#完整調(diào)用
def zsq(func):
def function():
print("gong neng diao yong")
func()
return function
def func1():
print("func1")
def func2():
print("func2")
def func3():
print("func4")
def func4():
print("func4")
func1=zsq(func1)
func1()
gong neng diao yong
func1
??好的,剛才我們耗費(fèi)了大量的精力,完成了一種在不改變?cè)泻瘮?shù)名稱的情況下,改掉了函數(shù)調(diào)用內(nèi)容的方法,不可謂不精巧,不可謂不奇妙。但是直到現(xiàn)在,之前的需求還是沒有完全滿足,因?yàn)榈浆F(xiàn)在為止,這個(gè)方法還是需要顯式的調(diào)用func1=zsq(func1)一次,才能完成預(yù)想的任務(wù)。那么有很多個(gè)funcN的時(shí)候,不是一樣需要手動(dòng)調(diào)用嗎?
??↓別急,python已經(jīng)為我們做了隱藏細(xì)節(jié)的處理,剛才的代碼,我們只要稍加改動(dòng),就能完成預(yù)想的效果:
def zsq(func):
def function():
print("gong neng diao yong")
func()
return function
@zsq
def func1():
print("func1")
def func2():
print("func2")
def func3():
print("func4")
def func4():
print("func4")
# func1=zsq(func1)
func1()
gong neng diao yong
func1
??↑其中,函數(shù)名稱上方的@XXX,就叫做裝飾器。

-
2-2?裝飾器—2個(gè)裝飾器
??裝飾順序:最靠近函數(shù)的最先解析,最原理函數(shù)的最后解析。
??why?
??代碼解析,自上而下。當(dāng)解析到@zsq1時(shí),python就要準(zhǔn)備為下面的函數(shù)進(jìn)行裝飾了,但是下面是不是函數(shù)?不是!
??下面是另一個(gè)裝飾器@zsq2,所以python只能跳過(guò)@zsq1,繼續(xù)看@zsq2能不能執(zhí)行裝飾,即@zsq2下面緊跟的是函數(shù)還是其他的裝飾器,這里是函數(shù),所以就先解析@zsq2了。
@zsq1
@zsq2
def name():
print("this is name function")
return "name"
print(name())
??↓以下假設(shè)調(diào)用的函數(shù)為func1 (),解析過(guò)程大致為:

??↑1、首先解析@zsq,此時(shí)函數(shù)
func1指向print("func1")

??↑2、此時(shí)函數(shù)已經(jīng)自動(dòng)調(diào)用并跳轉(zhuǎn)至zsq1函數(shù)內(nèi),python自動(dòng)把
func1的值賦給了funcY(這是裝飾器的工作機(jī)制),因此funcY指向print("func1")
??↑3、函數(shù)zsq1函數(shù)內(nèi)只有一條能執(zhí)行的語(yǔ)句return,執(zhí)行完return則zsq1函數(shù)執(zhí)行完畢。這相當(dāng)于python進(jìn)行的自動(dòng)調(diào)用zsq1完畢了,那么就要返回并執(zhí)行
func1()
??↑4、跳出zsq1函數(shù)后,
@zsq1功能已完成,這里我們暫時(shí)把@zsq1蓋住不看。跳出zsq1函數(shù)后,func1接收了zsq1函數(shù)的返回值(python解釋器自動(dòng)進(jìn)行的操作),實(shí)際上func1的指向已經(jīng)發(fā)生了改變,指向的是原zsq1內(nèi)部函數(shù)的代碼,如圖所示(注意這里手誤把“l(fā)alala gong neng2”錯(cuò)寫成了“gong neng diaoyong 2”)。
??↑5、當(dāng)系統(tǒng)想要開始執(zhí)行
func1()的代碼時(shí),發(fā)現(xiàn)還有一個(gè)裝飾器@zsq,則繼續(xù)開始解析@zsq。python將自動(dòng)調(diào)用zsq函數(shù)。同時(shí)把當(dāng)前的func1的值傳遞給了funcX。
??↑6、這里馬上要執(zhí)行return語(yǔ)句返回。

??↑7、zsq函數(shù)返回后,
func1的指向再次改變?yōu)閦sq函數(shù)的內(nèi)部函數(shù)為↓
print("gong neng diao yong1")
funcX()
??由上所知,funcX指向
print("lalalal gong neng2")
funcY()
??由上所知,funcY指向
print("func1")
??那么我們做一個(gè)等式的替換,把上述3段代碼進(jìn)行融合:
print("gong neng diao yong1")
funcX()
print("lalalal gong neng2")
funcY()
print("func1")
print("gong neng diao yong1")
print("lalalal gong neng2")
print("func1")
li@li-ThinkPad-T420s:~/Desktop/py$ cd /home/li/Desktop/py ; env PYTHONIOENCODING=UTF-8 PYTHONUNBUFFERED=1 /usr/bin/python3 /home/li/.vscode/extensions/ms-python.python-2019.8.30787/pythonFiles/ptvsd_launcher.py --default --client --host localhost --port 39427 /home/li/Desktop/py/ceshi=======.py
gong neng diao yong1
lalalal gong neng2
func1
li@li-ThinkPad-T420s:~/Desktop/py$
??↑正好和程序驗(yàn)證的執(zhí)行順序是一致的。
def zsq(funcX):
def function():
print("zsq work")
print("gong neng diao yong1")
funcX()
return function
def zsq1(funcY):
print("zsq1 work")
def function():
print("lalalal gong neng2")
funcY()
return function
@zsq
@zsq1
def func1():
print("func1")
def func2():
print("func2")
def func3():
print("func4")
def func4():
print("func4")
func1()
#保存完整代碼一段
??↓考慮只有返回值的情況
def zsq1(funX):
def zsq1in():
return "zsq1 " + funX()+" zsq1"
return zsq1in
def zsq2(funY):
def zsq2in():
return "zsq2 " +funY()+" zsq2"
return zsq2in
@zsq1
@zsq2
def name():
return "name"
print(name())
zsq1 zsq2 name zsq2 zsq1






??↓既有代碼,又有返回值
def zsq1(funX):
def zsq1in():
print("this is zsq1in function")
return "zsq1 " + funX()+" zsq1"
return zsq1in
def zsq2(funY):
def zsq2in():
print("this is zsq2in function")
return "zsq2 " +funY()+" zsq2"
return zsq2in
@zsq1
@zsq2
def name():
print("this is name function")
return "name"
print(name())
this is zsq1in function
this is zsq2in function
this is name function
zsq1 zsq2 name zsq2 zsq1






??↓三個(gè)裝飾器:
def zsq1(funX):
def zsq1in():
print("this is zsq1in function")
return "zsq1 " + funX()+" zsq1"
return zsq1in
def zsq2(funY):
def zsq2in():
print("this is zsq2in function")
return "zsq2 " +funY()+" zsq2"
return zsq2in
def zsq3(funZ):
def zsq3in():
print("this is zsq3in function")
return "zsq3 " +funZ()+" zsq3"
return zsq3in
@zsq1
@zsq2
@zsq3
def name():
print("this is name function")
return "name"
print(name())
this is zsq1in function
this is zsq2in function
this is zsq3in function
this is name function
zsq1 zsq2 zsq3 name zsq3 zsq2 zsq1
-
2-3?裝飾器—裝飾器的執(zhí)行時(shí)間
??↓注意看以下代碼,這里沒有調(diào)用任何函數(shù)
def zsq1(funX):
print("this is zsq1 function")
def zsq1in():
print("this is zsq1in function")
return "zsq1 " + funX()+" zsq1"
return zsq1in
def zsq2(funY):
print("this is zsq2 function")
def zsq2in():
print("this is zsq2in function")
return "zsq2 " +funY()+" zsq2"
return zsq2in
def zsq3(funZ):
print("this is zsq3 function")
def zsq3in():
print("this is zsq3in function")
return "zsq3 " +funZ()+" zsq3"
return zsq3in
@zsq1
@zsq2
@zsq3
def name():
print("this is name function")
return "name"
# print(name())
this is zsq3 function
this is zsq2 function
this is zsq1 function
??↑由此可知,裝飾在調(diào)用之前就已經(jīng)開始了。
-
2-4?裝飾器—重點(diǎn)強(qiáng)調(diào)
-
2-5?裝飾器—對(duì)有參數(shù)、無(wú)參數(shù)函數(shù)進(jìn)行裝飾
??↓對(duì)無(wú)參的函數(shù)裝飾
def zsq(fp):
print("this is zsq function")
def zsqin():
print("this is zsqinner function")
fp()
return zsqin
@zsq
def f1():
print("i am f1")
f1()
this is zsq function
this is zsqinner function
i am f1
??↓對(duì)有參的函數(shù)裝飾
def zsq(fp):
print("this is zsq function")
def zsqin():
print("this is zsqinner function")
fp(123)
return zsqin
@zsq
def f1(num):
print("i am "+str(num))
f1()
this is zsq function
this is zsqinner function
i am 123
??這樣做雖然程序上沒有錯(cuò)誤,但是你卻改變了原本函數(shù)的功能,因?yàn)樵瓉?lái)的函數(shù)要傳遞參數(shù),但是你現(xiàn)在調(diào)用卻不用參數(shù)。而且把參數(shù)的值的傳遞直接寫死到了裝飾器函數(shù)的內(nèi)函數(shù),也是沒有多少使用意義的。
??所以,原函數(shù)有參數(shù),裝飾器函數(shù)的內(nèi)函數(shù)也一定要有參數(shù)。
def zsq(fp):
print("this is zsq function")
def zsqin(num):
print("this is zsqinner function")
fp(num)
return zsqin
@zsq
def f1(num):
print("i am "+str(num))
f1(1)
??↓加入變長(zhǎng)參數(shù)的處理。
def zsq(fp):
print("this is zsq function")
def zsqin(*num):
print("this is zsqinner function")
fp(*num)
return zsqin
@zsq
def f1(a,b,c):
print("%d%d%d"%(a,b,c))
@zsq
def f2(a,b,c,d):
print("%d%d%d%d"%(a,b,c,d))
f1(1,2,3)
f2(1,2,3,4)
this is zsq function
this is zsq function
this is zsqinner function
123
this is zsqinner function
1234
-
2-6?裝飾器—對(duì)帶有返回值的函數(shù)進(jìn)行裝飾
def zsq(fp):
print("this is zsq function")
def zsqin():
print("this is zsqinner function")
ret =fp()
return ret
return zsqin
@zsq
def f1():
return "hahaha"
print(f1())
this is zsq function
this is zsqinner function
hahaha
-
2-7?裝飾器—通用裝飾器
def zsq(fp):
print("this is zsq function")
def zsqin(*args,**kwargs):
print("this is zsqinner function")
ret =fp(*args,**kwargs)
return ret
return zsqin
@zsq
def f1():
return "hahaha"
@zsq
def f2(a,b):
print(a)
print(b)
print(f1())
f2(1,2)
-
2-8?裝飾器—帶有參數(shù)的裝飾器
??還要再定義一個(gè)函數(shù)把原來(lái)的閉包套起來(lái)。
??python發(fā)現(xiàn)@裝飾器有參數(shù),先調(diào)用閉包的外層參數(shù)。
def zsq_arg(zhe):
print("this is zsq_arg function")
def zsq(fp):
print("this is zsq function")
def zsqin(*args,**kwargs):
print("this is zsqinner function")
ret =fp(*args,**kwargs)
return ret
return zsqin
return zsq
@zsq_arg("zheshisha")
def f1():
return "hahaha"
print(f1())
this is zsq_arg function
this is zsq function
this is zsqinner function
hahaha
??↑zsq_arg("zheshisha")相當(dāng)于一次執(zhí)行函數(shù),這個(gè)函數(shù)有個(gè)返回值,你返回誰(shuí),python就@誰(shuí),我覺得這里可以在內(nèi)部放多個(gè)裝飾器,然后根據(jù)傳入的參數(shù)來(lái)動(dòng)態(tài)選擇用哪一個(gè)裝飾器。