0.日常吐槽
十一小長假真的不要出去玩啊拧烦,你會見到你一年也見不到的那么多人忘闻,大概就是這樣的
這樣的
這樣的
所以還是安安靜靜待在宿舍好。
不說了恋博,我收拾東西準(zhǔn)備明天去天津了齐佳。。债沮。
接著上次的來
5. 內(nèi)嵌函數(shù)和閉包
所謂內(nèi)嵌函數(shù)就是函數(shù)內(nèi)部的函數(shù)炼吴。之前寫到,一個函數(shù)內(nèi)部就是一個空間疫衩,空間中的變量是局部變量硅蹦,一般不會與外部的變量發(fā)生關(guān)系。自然地闷煤,內(nèi)嵌函數(shù)的作用域也是外部函數(shù)的內(nèi)部空間童芹,也就是說內(nèi)嵌函數(shù)的調(diào)用只能在外部函數(shù)內(nèi)。
def fun1():
print('這是外部函數(shù)')
def fun2():
print('這是內(nèi)部函數(shù)')
fun2()
fun1()
================ RESTART: E:/Academics/python/codes/test3.py ================
這是外部函數(shù)
這是內(nèi)部函數(shù)
在函數(shù)外調(diào)用則會出錯
def fun1():
print('這是外部函數(shù)')
def fun2():
print('這是內(nèi)部函數(shù)')
fun2()
================ RESTART: E:\Academics\python\codes\test3.py ================
Traceback (most recent call last):
File "E:\Academics\python\codes\test3.py", line 5, in <module>
fun2()
NameError: name 'fun2' is not defined
5.1 數(shù)據(jù)的讀取
函數(shù)中可以讀取全局變量鲤拿,那么內(nèi)嵌函數(shù)能否讀取全局變量或者外部函數(shù)的局部變量假褪?如果兩者同時存在讀取的順序是什么樣的呢?下面做幾個實(shí)驗(yàn)近顷。
test = 0
def fun1():
def fun2():
print(test)
fun2()
fun1()
================ RESTART: E:\Academics\python\codes\test2.py ================
0
結(jié)論:內(nèi)嵌函數(shù)可以讀取全局變量生音。
def fun1():
test = 1
def fun2():
print(test)
fun2()
fun1()
================ RESTART: E:\Academics\python\codes\test2.py ================
1
結(jié)論:內(nèi)嵌函數(shù)可以讀取外部函數(shù)的局部變量宁否。
test = 0
def fun1():
test = 1
def fun2():
print(test)
fun2()
fun1()
================ RESTART: E:\Academics\python\codes\test2.py ================
1
test = 0
def fun1():
test = 1
def fun2():
test = 2
def fun3():
print(test)
fun3()
fun2()
fun1()
================ RESTART: E:\Academics\python\codes\test2.py ================
2
結(jié)論:內(nèi)嵌函數(shù)讀取變量時體現(xiàn)了一種“就近原則”。
5.2 nonlocal 與 global
global
聲明函數(shù)中的變量為全局變量缀遍,而nonlocal
代指上一層變量慕匠。
def fun1():
test = 1
def fun2():
nonlocal test
test = 2
print(test)
fun2()
print(test)
fun1()
================ RESTART: E:\Academics\python\codes\test2.py ================
1
2
再舉一個例子:
1. test = 0
2.
3. def fun1():
4. test = 1
5. def fun2():
6. global test
7. print(test)
8. test = 2
9. print(test)
10. fun2()
11.
12. print(test)
13. fun1()
14. print(test)
15. ================ RESTART: E:\Academics\python\codes\test2.py ================
16. 0
17. 1
18. 0
19. 2
簡單分析下幾次打印結(jié)果:
- 第一行定義了全局變量
test=0
,所以12行第一次打印結(jié)果為0 - 13行調(diào)用
fun1()
后域醇,首先fun1里定義局部變量test=1
絮重,接著13行打印局部變量test,得到第二次打印結(jié)果1 - fun1運(yùn)行到第10行調(diào)用
fun2()
歹苦,接著fun2聲明全局變量test=0
,從而第7行打印全局變量0 - fun2運(yùn)行到第八行將全局變量test修改為2督怜,至此fun1運(yùn)行完畢殴瘦,釋放所有內(nèi)存。14行打印全局變量test号杠,結(jié)果為2蚪腋。
總之,nonlocal
允許內(nèi)嵌函數(shù)讀取并修改上一層環(huán)境中的局部or全局變量姨蟋,而global
則是直接跳到最外層讀取和修改全局變量屉凯。
自然地有一個問題,要是想往外跳兩層怎么辦眼溶?我試驗(yàn)了一下使用兩個nonlocal
是可以的悠砚,像下面這樣:
test = 0
def fun1():
test = 1
def fun2():
nonlocal test
def fun3():
nonlocal test
test = 3
fun3()
print(test)
fun2()
print(test)
fun1()
================ RESTART: E:\Academics\python\codes\test2.py ================
3
3
或者干脆fun2里什么都不寫也可以,如下:
test = 0
def fun1():
test = 1
def fun2():
def fun3():
nonlocal test
test = 3
fun3()
print(test)
fun2()
print(test)
fun1()
================ RESTART: E:\Academics\python\codes\test2.py ================
3
3
上面這個就很神奇堂飞,看上去像是運(yùn)行fun3時nonlocal
向上一層找test沒有找到灌旧,然后就自動找到了再上一層即fun1中的test=1
,然后修改了這個值绰筛。
無論哪一個做法都避免不了要干擾到fun2枢泰,不知沒有直接讓fun3去修改fun1中的值的辦法,而保留fun2中的test=2不受影響铝噩?如果有大神看到這里知道怎么做的話希望不吝賜教衡蚂!
5.3 閉包(closure)
閉包其實(shí)我也沒太搞懂,就先簡單寫點(diǎn)骏庸,等弄懂了再來補(bǔ)充毛甲。
閉包從表現(xiàn)形式上定義為:如果在一個內(nèi)部函數(shù)里對外部作用域(但不是全局作用域)的變量進(jìn)行引用,并且外部函數(shù)的返回值是內(nèi)部函數(shù)具被,那么內(nèi)部函數(shù)就成稱為閉包丽啡。
一般情況下,在我們認(rèn)知當(dāng)中硬猫,如果一個函數(shù)結(jié)束补箍,函數(shù)的內(nèi)部所有東西都會釋放掉改执,還給內(nèi)存,局部變量都會消失坑雅。但是閉包是一種特殊情況辈挂,如果外函數(shù)在結(jié)束的時候發(fā)現(xiàn)有自己的臨時變量將來會在內(nèi)部函數(shù)中用到,就把這個臨時變量綁定給了內(nèi)部函數(shù)裹粤,然后自己再結(jié)束终蒂。
def FunX(x):
def FunY(y):
return x * y
return FunY
此時FunY
就稱為閉包。如果對FunX
進(jìn)行調(diào)用:
>>> i = FunX(8)
>>> type(i)
<class 'function'>
>>> i(5)
40
也可以直接寫成:
>>> FunX(8)(5)
40
總結(jié)一下遥诉,閉包主要的兩個特點(diǎn)是:
- 外部函數(shù)返回內(nèi)部函數(shù)的引用
- 外部函數(shù)把臨時函數(shù)綁定給內(nèi)部函數(shù)
- 調(diào)用完外部函數(shù)后內(nèi)存未釋放
6. lambda表達(dá)式創(chuàng)建匿名函數(shù)
lambda
表達(dá)式的作用就是創(chuàng)建一個匿名函數(shù)拇泣,語法為lambda (parameter): (return value)
。例如:
>>> f = lambda x,y : x+y
>>> f(2,3)
5
6.1 filter過濾器
filter(function or None, iterable)
函數(shù)有兩個參數(shù)矮锈,第一個參數(shù)是函數(shù)或None
霉翔,第二個參數(shù)是用于迭代的對象。當(dāng)?shù)谝粋€參數(shù)是None時苞笨,則返回迭代對象中True的值债朵;當(dāng)?shù)谝粋€參數(shù)是函數(shù)時,則將迭代對象代入函數(shù)進(jìn)行運(yùn)算瀑凝,并返回結(jié)果為True的原始值序芦。因此filter可以和lambda函數(shù)結(jié)合起來使用。filter返回的是一個迭代對象粤咪,可用list
或tuple
等將其列舉出來谚中。
>>> a = filter(None , [1,0,True,False])
>>> list(a)
[1, True]
一個篩選奇數(shù)的程序
>>> num = range(10)
>>> list(filter(lambda x : x%2 , num))
[1, 3, 5, 7, 9]
6.2 map映射
map(function, iterable)
函數(shù)返回迭代對象經(jīng)過函數(shù)運(yùn)算后的值。同樣可以和lambda表達(dá)式巧妙結(jié)合寥枝。
>>> list(map(lambda x : x**2 , range(10)))
[0, 1, 4, 9, 16, 25, 36, 49, 64, 81]
7.遞歸
在其他編程語言也常使用遞歸的方法藏杖。所謂遞歸便是在一個函數(shù)中調(diào)用它自身。遞歸最重要的是要有一個停止的返回值脉顿,否則會陷入死循環(huán)蝌麸。寫幾個遞歸常見的例子:
- 求階乘:
def factorial(n):
if n == 1:
return n
else:
return n * factorial(n-1)
number = int(input('請輸入一個正整數(shù):'))
result = factorial(number)
print('%d的階乘是:%d' % (number, result))
============== RESTART: E:/Academics/python/codes/factorial.py ==============
請輸入一個正整數(shù):6
6的階乘是:720
- 求Fibonacci數(shù)列
def fib(n):
if n==1 or n==2:
return 1
else:
return fib(n-1) + fib(n-2)
number = int(input('請輸入一個正整數(shù):'))
result = fib(number)
print('a(%d) = %d' % (number , result))
============== RESTART: E:/Academics/python/codes/Fibonacci.py ==============
請輸入一個正整數(shù):3
a(3) = 2
>>>
============== RESTART: E:/Academics/python/codes/Fibonacci.py ==============
請輸入一個正整數(shù):5
a(5) = 5
>>>
============== RESTART: E:/Academics/python/codes/Fibonacci.py ==============
請輸入一個正整數(shù):25
a(25) = 75025
- 漢諾塔
這是遞歸算法里十分經(jīng)典的例子了。漢諾塔的基本規(guī)則是:將在柱X上的圓盤移到柱Z上面艾疟,每次只能移動任何一個柱子上面的一個圓盤来吩,且必須保證小的在上大的在下。
def hanoi(n,x,y,z):
if n==1:
print(x, '-->',z)
else:
hanoi(n-1,x,z,y) #將前n-1個圓盤從X移動到Y(jié)
print(x,'-->',z) #將最后一個圓盤從X移動到Z
hanoi(n-1,y,x,z) #將Y上的n-1個圓盤移動到Z
number=int(input('請輸入圓盤個數(shù):'))
hanoi(number,'X','Y','Z')
================ RESTART: E:/Academics/python/codes/hanoi.py ================
請輸入圓盤個數(shù):3
X --> Z
X --> Y
Z --> Y
X --> Z
Y --> X
Y --> Z
X --> Z
遞歸算法比較簡練蔽莱,也很適用于解決一些復(fù)雜問題弟疆。但缺點(diǎn)也是明顯的,就是它會非常占用內(nèi)存盗冷,比如Fibonacci數(shù)列里n稍微大一點(diǎn)程序就崩掉了怠苔。所有有時候老老實(shí)實(shí)用迭代還是蠻好的,也好理解仪糖,就是長了點(diǎn)而已柑司。
往期回顧
Python學(xué)習(xí)筆記(0)之Hello,python!
Python學(xué)習(xí)筆記(1)之列表list
Python學(xué)習(xí)筆記(2)之元組迫肖、字典&集合
Python學(xué)習(xí)筆記(3)之字符串string
Python學(xué)習(xí)筆記(4)之函數(shù)(一)