一個函數中存在另一個函數(定義/調用),這種方式咱們稱之爲函數嵌套。因此:函數的嵌套主要分爲嵌套調用
,以及嵌套定義
。編程
函數的嵌套調用 def max2(a,b): # 判斷兩個變量的最大值 return a if a > b else b def max4(a,b,c,d): # 判斷四個變量的最大值 res1 = max2(a,b) # 函數的嵌套調用 res2 = max2(res1,c) res3 = max(res2,d) print(res3) max4(10,100,21,99) 函數的嵌套定義 def func1(): print('from func1') def func2(): print('from func2') def func3(): print('from func3') func3() # 只有在func2中才能調用內部定義的函數func3 func2() func1()
注意:在函數的內部定義函數,只能在函數內部進行調用,在其餘地方是沒法進行調用,強行調用就會提示NameError異常,因此說函數是有可見範圍的,這就涉及到了做用域了閉包
一個標識符的可見範圍,叫作標識符的做用域。通常常說的是變量的做用域。根據做用的範圍主要分爲全局做用域
和局部做用域
。app
x = 1 # 全局變量 def outer(): def inner(): y = 100 # 局部變量 print(x) inner() outer() print(y)
觀察下面的例子:dom
x = 1 def outer(): def inner(): x += 1 return x inner() outer()
代碼是從上到下執行的,所欲這樣寫也沒什麼毛病,可是這裏這個例子是沒法執行的,爲何呢?編程語言
函數是做爲一個總體一塊兒被解釋的
。x += 1
),那麼它就不會在調用全局變量x,而是標識x是局部定義的變量如何解決呢?有兩種方法:更換變量名稱
、聲明當前變量非本地變量(global
)函數
x = 1 def outer(): def inner(): y = x + 1 # 這裏定義的y是局部變量,而x來自於全局變量 return y return inner() print(outer())
咱們經過在函數內部使用global關鍵字來聲明一個變量不是局部變量,而是一個全局變量。ui
def outer(): def inner(): global x # 在函數內部聲明一個全局變量,全局不存在時新建全局變量x,全局變量x存在時,則使用全局變量x x = 10 # 修改全局變量x的值 inner() outer() print(x)
雖然全局變量x,在全局沒有被定義,可是因爲在函數內部使用了global關鍵字,因此x就變成了全局變量了。使用了global關鍵字,那麼以前的例子就能夠進行以下修改了指針
x = 1 def outer(): def inner(): global x # 使用全局變量x x += 1 # 這裏的x是全局變量,那麼對x的修改必然會做用域全局 return x inner() outer() print(x) # 2 , 在函數內部把全局變量x給修改了!!!
針對global的總結:code
不要使用global
。 在不少編程語言中都存在閉包的概念,那什麼是閉包呢?閉包其實就是一個概念,出如今嵌套函數中,指的是:內層函數引用到了外層函數的自由變量
,就造成了閉包
自由變量:未在本地做用域中定義的變量,好比在嵌套函數的外層定義的變量(非全局變量),對內層來講,這個變量就叫作自由變量。
def outer(): c = [1] def inner(): c[0] = 1 return c return inner() a = outer() print(a)
注意:上面這個例子比較特殊,首先它是一個閉包,在inner函數內引用了外層函數的自由變量C。由於這裏的c是一個引用類型,咱們能夠直接經過c來操做c中的元素,可是沒辦法對c自己進行修改,即c += [1,3]
。看似是列表拼接,可是它會從新對c進行聲明,這就引起了以前的問題,內部函數inner沒有定義c,因此會報錯!因此當c不是引用類型的話,咱們就沒辦法修改了嗎?固然不是,可使用global把c聲明爲全局變量,可是這就不是閉包了,因此這裏就須要使用nonlocal
了(python 3 特有)。
疑問?咱們都說函數執行完畢後,函數的內部變量將會被回收,這裏的outer執行完畢後,那麼變量c應該會被回收啊,爲何還能被內層的inner找到呢?這是由於在定義階段,解釋器解釋到inner函數時,因爲函數是做爲一個總體被解析的,因此解釋器知道在inner內部引用了外部的變量,因此在執行函數outer時,並不會回收已被內部函數inner引用的自由變量c。
使用了nonlocal關鍵字,將變量標記爲不在本地做用域定義,而在上一級局部做用域中定義,但不能是全局做用域中定義。
nonlocal只能用在嵌套函數的內部
def outer(): c = 100 def inner(): nonlocal c # 聲明不是本地的c(引用上級目錄的c) c += 200 # 對c進行修改 return c print('內',c) # 100 c = 1000 return inner a = outer() print('外',a) # 1200
在Python中,一切皆對象,函數也不列外,當咱們給函數定義默認值時,Python會把它存放在函數的屬性中,這個屬性值就伴隨這個函數對象的整個生命週期。
foo.__defaults__屬性查看函數的默認值屬性
In [77]: import random ...: ...: def add(x=set(),y=[]): ...: x.add(random.randint(1,10)) ...: y.append(1) ...: # print(x) ...: # print(y) ...: ...: print(add(),id(add)) ...: print(add.__defaults__) ...: ...: print(add(),id(add)) ...: print(add.__defaults__) None 2721081985904 ({1}, [1]) None 2721081985904 ({1, 10}, [1, 1])
仔細查看輸出結果,發現函數地址沒有變,也就是說函數這個對象沒有變,可是咱們發現每次它的__default__屬性都會發生變化,這是爲何呢?這是由於sed和list的默認值都是引用類型,它們引用的都是函數在定義時定義的默認值中。 雖然函數執行完就釋放了內存空間,也是因爲引用類型,指向默認空間的指針沒了,可是已經在調用時改變了默認值空間的對象中的元素,因此在下一次再次調用時此時默認值空間的元素已經被改變了。因此當函數的默認值爲引用類型時,這點要特別的注意了
解決辦法:
變量的解析原則,也能夠理解爲變量的查找順序:
Local
): 本地做用域、局部做用域的local命名空間。函數調用是建立,調用結束消亡Enclosing
): Python 2.2時引入嵌套函數,實現了閉包,這個就是嵌套函數的外部函數的命名空間Global
): 全局做用域,即一個模塊的命名空間。模塊被import時建立,解釋器退出時消亡B(Build-in
): 內置模塊的命名空間,生命週期從Python解釋器啓動時建立到解釋器退出時消亡。例如print函數、open函數等。
變量查找的規則爲 L > E > G > B,即:先本地後嵌套再全局最後是內置函數中
全局函數:
局部函數: