Python學(xué)習(xí)筆記(5)之函數(shù)(二)

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é)果:

  1. 第一行定義了全局變量test=0,所以12行第一次打印結(jié)果為0
  2. 13行調(diào)用fun1()后域醇,首先fun1里定義局部變量test=1絮重,接著13行打印局部變量test,得到第二次打印結(jié)果1
  3. fun1運(yùn)行到第10行調(diào)用fun2()歹苦,接著fun2聲明全局變量test=0,從而第7行打印全局變量0
  4. 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)是:

  1. 外部函數(shù)返回內(nèi)部函數(shù)的引用
  2. 外部函數(shù)把臨時函數(shù)綁定給內(nèi)部函數(shù)
  3. 調(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返回的是一個迭代對象粤咪,可用listtuple等將其列舉出來谚中。

>>> 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)蝌麸。寫幾個遞歸常見的例子:

  1. 求階乘:
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
  1. 求Fibonacci數(shù)列

a_1=a_2=1
a_n=a_{n-1}+a_{n-2},n>2

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
  1. 漢諾塔

這是遞歸算法里十分經(jīng)典的例子了。漢諾塔的基本規(guī)則是:將在柱X上的圓盤移到柱Z上面艾疟,每次只能移動任何一個柱子上面的一個圓盤来吩,且必須保證小的在上大的在下。

Tower of Hanoi
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ù)(一)

最后編輯于
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請聯(lián)系作者
  • 序言:七十年代末,一起剝皮案震驚了整個濱河市攒驰,隨后出現(xiàn)的幾起案子蟆湖,更是在濱河造成了極大的恐慌,老刑警劉巖玻粪,帶你破解...
    沈念sama閱讀 216,402評論 6 499
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件隅津,死亡現(xiàn)場離奇詭異,居然都是意外死亡劲室,警方通過查閱死者的電腦和手機(jī)伦仍,發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 92,377評論 3 392
  • 文/潘曉璐 我一進(jìn)店門,熙熙樓的掌柜王于貴愁眉苦臉地迎上來很洋,“玉大人充蓝,你說我怎么就攤上這事《撞” “怎么了?”我有些...
    開封第一講書人閱讀 162,483評論 0 353
  • 文/不壞的土叔 我叫張陵悠垛,是天一觀的道長线定。 經(jīng)常有香客問我,道長确买,這世上最難降的妖魔是什么斤讥? 我笑而不...
    開封第一講書人閱讀 58,165評論 1 292
  • 正文 為了忘掉前任,我火速辦了婚禮湾趾,結(jié)果婚禮上芭商,老公的妹妹穿的比我還像新娘。我一直安慰自己搀缠,他們只是感情好铛楣,可當(dāng)我...
    茶點(diǎn)故事閱讀 67,176評論 6 388
  • 文/花漫 我一把揭開白布。 她就那樣靜靜地躺著艺普,像睡著了一般簸州。 火紅的嫁衣襯著肌膚如雪。 梳的紋絲不亂的頭發(fā)上歧譬,一...
    開封第一講書人閱讀 51,146評論 1 297
  • 那天岸浑,我揣著相機(jī)與錄音,去河邊找鬼瑰步。 笑死矢洲,一個胖子當(dāng)著我的面吹牛,可吹牛的內(nèi)容都是我干的缩焦。 我是一名探鬼主播读虏,決...
    沈念sama閱讀 40,032評論 3 417
  • 文/蒼蘭香墨 我猛地睜開眼责静,長吁一口氣:“原來是場噩夢啊……” “哼!你這毒婦竟也來了掘譬?” 一聲冷哼從身側(cè)響起泰演,我...
    開封第一講書人閱讀 38,896評論 0 274
  • 序言:老撾萬榮一對情侶失蹤,失蹤者是張志新(化名)和其女友劉穎葱轩,沒想到半個月后睦焕,有當(dāng)?shù)厝嗽跇淞掷锇l(fā)現(xiàn)了一具尸體,經(jīng)...
    沈念sama閱讀 45,311評論 1 310
  • 正文 獨(dú)居荒郊野嶺守林人離奇死亡靴拱,尸身上長有42處帶血的膿包…… 初始之章·張勛 以下內(nèi)容為張勛視角 年9月15日...
    茶點(diǎn)故事閱讀 37,536評論 2 332
  • 正文 我和宋清朗相戀三年垃喊,在試婚紗的時候發(fā)現(xiàn)自己被綠了。 大學(xué)時的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片袜炕。...
    茶點(diǎn)故事閱讀 39,696評論 1 348
  • 序言:一個原本活蹦亂跳的男人離奇死亡本谜,死狀恐怖,靈堂內(nèi)的尸體忽然破棺而出偎窘,到底是詐尸還是另有隱情乌助,我是刑警寧澤,帶...
    沈念sama閱讀 35,413評論 5 343
  • 正文 年R本政府宣布陌知,位于F島的核電站他托,受9級特大地震影響,放射性物質(zhì)發(fā)生泄漏仆葡。R本人自食惡果不足惜赏参,卻給世界環(huán)境...
    茶點(diǎn)故事閱讀 41,008評論 3 325
  • 文/蒙蒙 一、第九天 我趴在偏房一處隱蔽的房頂上張望沿盅。 院中可真熱鬧把篓,春花似錦、人聲如沸腰涧。這莊子的主人今日做“春日...
    開封第一講書人閱讀 31,659評論 0 22
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽窖铡。三九已至揍很,卻和暖如春,著一層夾襖步出監(jiān)牢的瞬間万伤,已是汗流浹背窒悔。 一陣腳步聲響...
    開封第一講書人閱讀 32,815評論 1 269
  • 我被黑心中介騙來泰國打工, 沒想到剛下飛機(jī)就差點(diǎn)兒被人妖公主榨干…… 1. 我叫王不留敌买,地道東北人简珠。 一個月前我還...
    沈念sama閱讀 47,698評論 2 368
  • 正文 我出身青樓,卻偏偏與公主長得像,于是被迫代替她去往敵國和親聋庵。 傳聞我的和親對象是個殘疾皇子膘融,可洞房花燭夜當(dāng)晚...
    茶點(diǎn)故事閱讀 44,592評論 2 353

推薦閱讀更多精彩內(nèi)容

  • 原諒我到這么遲才開始寫2016年的年度總結(jié)。 這一年祭玉,我18歲 氧映。這一年,我仿佛走過了狗血電視劇中常走的套路脱货。這...
    大花癡與小寧靜閱讀 230評論 0 1
  • 自己內(nèi)心演繹了一場撕逼大戲岛都。 故事情節(jié)一,外表風(fēng)平浪靜振峻,你心儀演繹果1萬場戲的某人臼疫。抓狂中。 故事情節(jié)二扣孟,剛才又喝...
    曼曼風(fēng)雨閱讀 68評論 0 0
  • 忽然間嗓子疼的難以入睡烫堤,凡間事太多,好想林間憩幾天凤价,終是要面對鸽斟,還是省了林間憩,兒孫自有兒孫福利诺,我還是看書富蓄,追劇,...
    黑妹wln閱讀 147評論 0 0
  • 1 最熟悉的老人立轧,應(yīng)該就是他了格粪,老尹躏吊,比我大了30歲氛改。 心懷忐忑卻故意漫不經(jīng)心地問他,能不能成為我文章主角的時候比伏,...
    嬰兒看世界閱讀 559評論 10 16
  • 7月3日 星期二 晴 我的導(dǎo)師說:愛情最重要屬性是什么胜卤?是要“開心”。如果你愛的人只會給你煩惱赁项,讓你痛苦葛躏,那么...
    能量女王劉大紅閱讀 194評論 0 0