15.函式的返回值與巢狀函式
(十七)函式的返回值與巢狀函式
1:什麼是返回值
返回值指的是函式返回的結果 # 示例1 def add(x, y): print(x, y) return x + y # 返回值, return以後的語句將不會再執行 print(x) print(y) add(1,2) # 示例2 def add(x, y): print(x, y) return x + y # 函式體內,碰到return語句,函式執行完畢,之後的語句將不會再執行return x - y # 不會再次執行 add(1,2)
2:函式的隱式返回和顯示返回
Python函式都有返回值,如果函式體內沒有return語句,那麼預設返回None,稱為隱式返回
如果有return語句,直接返回,稱為顯示返回
3:函式多值返回
函式可以返回多個值 def get_data(x,y,z): return x + 1, y + 1, z + 1 print(get_data(1,2,3)) # 得到的是一個元組 def get_data(x,y,z): return[x + 1, y + 1, z + 1] print(get_data(1,2,3)) # 得到的是一個列表
4:什麼是巢狀函式
def outer_function(): # 內部函式 def inner_function(): print("inner_function") print("outer_function") # 內部呼叫 inner_function() outer_function() 注意:inner_function函式只能在outer_function函式內部呼叫,無法再outer_function函式外部被呼叫
5:函式作用域
什麼是作用域:作用域指的是,一個識別符號的作用範圍就是這個識別符號的作用域 # 示例1: z變數只能在函式內部被訪問無法在函式外部被訪問,z變數的作用域就是add函式內部 def add(x, y): z = 100 print(x, y , z) add(1,2) print(z) # 示例2:z可以在add函式外部被訪問到, z = 100 def add(x, y): print(x, y , z) add(1,2) print(z) 小結: 全域性作用域:整個執行環境中都可見,也就是整個模組內可見 區域性作用域:函式或類中可見,函式體或類就是作用域
6:函式的閉包
# 對比下列中的函式 # 示例1 x=5 def add(): y = x + 1 add() # 示例2 x=5 def add(): x = x + 1 # 報錯:local variable 'x' referenced before assignment add() 報錯原因分析: 在示例1中,函式內部引用的x為函式外部的x, 因此x的值為5 在示例2中,函式內部重新定義了x的值,那麼整個函式內部都會使用這個內部x,因此在運算x + 1的時候,x還沒有完成定義就被引用了,這裡的x引用的一定是內部正在定義的x,不是函式外部的x=5 # 示例3: x=5 def add(): print(x) # 這裡的x引用的是x = 1的x,但是執行print(x)時,x還沒有被定義,所以報錯:local variable 'x' referenced before assignment x = 1 add() # 4 閉包: 內部函式引用了外部函式的變數,這就是閉包的定義 def outer_function(): x = 100 def inner_function(): print(x) # 內部函式引用了外部函式的自由變數 return inner_function ret = outer_function() ret()
7:nonlocal關鍵字、global關鍵字
# 1:global關鍵字 x=5 def add(): global x # 使用global關鍵字,指定x的引用全域性的x變數 x = x + 1 add() # global關鍵字對全域性變數的汙染,因此需要慎用 x = 100 def foo(): global x # x被聲明瞭全域性變數 x = 10 x += 1 print(x) # x的值為11 foo() print(x) # x的值為11, 因此global關鍵字汙染了全域性變數x # 2:nonlocal關鍵字 def outer_function(): x = 100 def inner_function(): x = x + 1 # 這樣會報錯 print(x) return inner_function ret = outer_function() ret() # 修改上面的程式碼,使用nonlocal關鍵字 def outer_function(): x = 100 def inner_function(): nonlocal x x = x + 1 # 這樣會報錯 print(x) return inner_function ret = outer_function() ret() 注意:nonlocal關鍵字的意義在於:內部函式的變數引用的外部函式變數的值,不是全域性作用域的值,因此不會汙染全域性作用域
8:函式預設值的作用域
# 示例, 函式的預設值繫結在函式物件的整個生命週期,不會因為函式內部對預設值操作而發生改變 def add(lst = []): lst.append('hello') print(lst) add() # 輸出:['hello'] print('id={}'.format(id(add))) # 函式物件的id值不變,呼叫的是同一個函式 add() # 輸出['hello', 'hello'] print('id={}'.format(id(add))) # 函式物件的id值不變,呼叫的是同一個函式 # 檢視函式的位置引數的預設值 print(add.__defaults__) # 檢視函式的關鍵字引數的預設值 print(add.__kwdefaults__) # 可以使用兩種方法解決函式預設值帶來的弊端: # 1:淺拷貝 def add(lst = []): lst = lst[:] lst.append('hello') print(lst) add() # 輸出:['hello'] add() # 輸出:['hello'] # 2:引數值判斷 def add(lst=None): if lst is None: lst = [] lst.append(1) print(lst) add() print(add.__defaults__) add() print(add.__defaults__) add([1,2,3]) print(add.__defaults__) add([4,5,6]) print(add.__defaults__)
9:函式銷燬
1:函式執行結束時銷燬 2:del 刪除函式物件 3:重新命名覆蓋函式物件 def add(): print('add') del add add()
1.什麼是返回值
-
返回值指的是函式返回的結果;
-
return執行完畢後面的語句將不會再執行;
-
如果一個函式裡面有兩個return,前面return執行完畢,後面的return也不會執行;
2.函式的隱式返回和顯示返回
-
Python函式都有返回值,如果有return語句,是顯式返回;
-
如果沒有return語句,預設返回None,是隱式返回;
3.函式多值返回
-
如果返回多個值通過逗號分開,會把值進行壓縮,封裝成一個元組;
-
如果返回一個列表,得到的就是一個列表;
4.什麼是巢狀函式
-
巢狀函式的內部函式只能在包含它的函式的直接父級呼叫,也就是隻能在包含它的外部函式中呼叫;
-
巢狀函式層數不宜過深,一般3層以內即可,太深不夠直觀,容易造成程式碼混亂;
5.函式作用域
-
作用域指的是,一個識別符號的作用範圍就是這個識別符號的作用域;
-
在函式裡面定義的變數(即區域性作用域)只能在函式體內部被訪問,函式外部不能訪問;
-
在函式外部定義的變數(即全域性作用域)能在函式體外部被訪問,也能在函式體內部被訪問;
6.函式的閉包
-
閉包:內部函式引用了外部函式的變數,這就是閉包的定義;
-
如果函式體想訪問變數,只能在變數定義之後才能訪問;
-
如果要訪問函式體內部的函式,可以先把內部函式的函式名作為外部函式的返回值,把外部函式的引用賦值給變數,再呼叫變數;
7.關鍵字
-
global關鍵字:可以指定變數為全域性變數,但是global關鍵字會汙染全域性變數,也就是會覆蓋之前全域性變數的值,所以最好慎用;
-
nonlocal關鍵字:可以申明內部函式的變數引用的是外部函式變數的值(作用域在外部函式),不是全域性作用域的值,因此不會汙染全域性作用域;
8.函式預設值的作用域
-
同一個函式的生命週期相同,函式的預設值會繫結在函式的整個生命週期上,不會因為函式內部對預設值的操作而發生改變;
-
可以使用淺拷貝copy(簡寫[:])來清空預設值,那每次呼叫函式,預設值都為初始值;
-
也可以通過引數值判斷來給預設值重新賦值,那每次呼叫函式,預設值都為初始值;
9.函式銷燬
- 可以通過 del 函式名 的方式來刪除函式,再呼叫函式時,就會報錯不存在;
課後補充:
問題:閉包的具體使用?
答:閉包的理解就是在函式a裡面再巢狀一個函式b等,那麼函式b裡面都可以引用函式a的變數,然後函式a返回值是函式b,這樣就形成了閉包,這樣就相當於說,這個函式b就是一個封閉的函式,只能在a函式中使用。
閉包的具體使用最好的例子就是後面的裝飾器(21節:https://www.9xkd.com/user/plan-view.html?id=1603899338)
問題:巢狀函式的作用?
巢狀函式很方便,比如:一個函式裡面有很多相同程式碼,但是其他地方不需要用,這個時候不需要定義公用函式,可以直接在這個地方定義一個內部函式,方便僅在當前這個函式裡面公用;
子函式裡面可以直接引用上層函式的變數,也可以直接引用全域性變數;
巢狀還有一個好處就是,在各個函式裡面定義的變數,另外的函式互相不會影響(巢狀一般也不要巢狀很多層,最好不超過3層);
問題:關於global關鍵字具體怎麼使用呢?
答:global關鍵字的作用就是把區域性變數修改為全域性變數,它會覆蓋原有的全域性變數,所以我們要慎用global,能不用就不用,避免造成全域性汙染; 如下舉個例子講解一下,如下圖所示:
fun2中變數c現在是區域性變數,我們如果通過global宣告c之後,就全域性都可以呼叫c了,也就相當於c變成了全域性變數;
fun3中,如果一個函式已經聲明瞭一個區域性變數c,並且賦值了999。然後在這個函式裡面,再使用global 宣告一個全域性變數c,那麼後面的子級函式和外部引用c都是引用的全域性變數c的值;
但是如果我們在同級函式中引用c的話,引用的依舊是區域性變數c的值。
是不是感覺有點亂,所以,我們為了避免混亂,建議不要聲明瞭區域性變數後,又吧它改為全域性變數的形式。也就是說在同一級中,不要聲明瞭區域性變數c後,然後又宣告全域性變數c,如下,這樣是錯的:
問題:nonlocal關鍵字的具體使用?
答:如下圖所示:nonlocal關鍵字用來標識變數所在的名稱空間,用在巢狀函式中,表示外層;
在外層的變數a為全域性變數,不屬於巢狀函式,所以,用不能在函式外面使用nonlocal a,這樣會報錯(總之nonlocal不能引用全域性變數);
在fun1中b屬於巢狀函式中的外層函式的變數,所以,在fun2中使用nonlocal修改b是沒有問題的;
nonlocal是可以隔層的,不一定需要是直接的父層,例如:我在fun1中定義的b,在fun3中的nonlocal也可以宣告修改b,當然fun2中也可以修改,並且如果是在fun3中修改b,那使用時就會以fun2中的b的執行結果為初始值(也就是初始值不在是fun1中b的值了);
nonlocal的作用就是在巢狀函式中使用,對上級函式的變數進行申明修改;