第10天 函式詳解
阿新 • • 發佈:2018-12-15
一. 人生三問
1. 什麼是函式 函式就是用def關鍵字宣告的為了實現一組功能的程式碼塊。 2. 為什麼要用函式 在開發程式的過程中,我們可能會遇到一些相同的功能,為了避免寫重複的程式碼,從而出現了函式。 3. 怎麼使用函式 函式分為定義和呼叫兩個階段,函式名()就是呼叫函式的過程
# 函式的定義
def fun1(x, y, z):
'''註釋'''
code1
code2
return x, y, z
# 函式呼叫
fun1(1, 2, 3)
二. 函式的使用原則
# 函式在呼叫過程中必須要先定義才能進行使用1. 函式的定義階段 只檢測函式體中是否存在語法錯誤2. 函式的呼叫階段 呼叫過程才會去真正的執行程式碼塊,檢視是否有邏輯錯誤
測試:
def fun1(): print('from fun1') bar() # 當執行fun1程式碼的時候發現要執行bar # 但是bar函式還沒有定義,所以報錯 fun1() def bar(): print('from bar')View Code
1. 函式的三種定義方式
1. 無參函式 2. 有參函式 3. 空函式
2. 函式的三種呼叫方式
1. 語句的形式 =====》 register() 2. 表示式呼叫========》 res = register() * 123. 作為引數傳遞呼叫 ===========》
3. return的特點
功能 1. 可以返回值 1.1 函式的返回值沒有型別的限制 1.2 函式的返回值沒有個數的限制 <1>.返回的是一個值就是函式本身 <2>.返回多個值就是元祖 <3>.返回0個值或者乾脆沒有就返回None2. 可以結束函式的執行 函式內可以有多個return,只要執行了任意一個return函式就結束了
三. 引數詳解
引數的分類:
1. 形式引數(形參) 在函式定義階段,傳遞的引數就是形參
2. 實際引數(實參) 在函式呼叫階段,傳遞的引數就是實參細分: 1. 位置引數 2. 關鍵字引數(預設形參) 3. 可變長引數
# 這個是函式的定義階段 # 裡面的引數x, y, z是函式的形參 def fun(x, y, z): print('hhhh') # 這個是函式的呼叫階段 # 裡面的引數1,2,3是實參 fun(1, 2, 3)
1. 位置引數
位置引數:1. 位置實參 在呼叫階段,按照從左到右的順序依次定義的引數叫做位置實參 # 特點:與形參的值一一對應
2. 位置形參 在定義階段,按照從左到右的順序依次定義的引數叫做位置形參 # 特點:必須被傳值,多一個不行,少一個不行
驗證:
# 形參和實參的值必須一一對應 def fun1(x, y, z): print(x, y, z) fun1(1) # 會報錯 fun1(1, 2, 3) fun1(1, 2, 3, 4) # 會報錯
2. 關鍵字引數
1. 關鍵字實參 在函式呼叫階段,按照key=value的形式定義的實參叫做關鍵字實參 # 特點: # 1. 關鍵字實參必須放在位置實參的後面 # 2. 不能給同一個形參賦多個值2. 關鍵字形參(預設形參) 在函式定義階段,按照key=value的形式定義的形參叫做關鍵字形參。我們一般稱之位預設形參 # 特點: # 1. 在定義階段已經有值,意味著在呼叫階段可以不用傳值 # 2. 位置形參必須放在預設形參的前面 # 3. 預設形參的值在定義階段已經固定死了,之後的更改不會影響 # 4. 預設形參的值通常應該是不可變型別
驗證:關鍵字引數
# 關鍵字引數必須放在位置引數的後面 def fun1(x, y, z): print(x, y, z) fun1(1, y=2, 3) # 報錯 fun1(1, 2, z=3)關鍵字引數必須放在位置引數的後面
# 不能給同一個形參傳遞多個值 def fun1(x, y, z): print(x, y, z) fun1(1, 2, 3, x=1)# 會報錯不能給同一個形參傳遞多個值
驗證:預設形參
# 呼叫階段可以不用傳值 def func1(x, y, z=2): print(x, y, z) func1(1, 2)呼叫階段可以不用傳值
# 位置形參必須在預設形參之前 def func1(x,z=2, y):# 會報錯 print(x, y, z) func1(1, 2)位置形參必須在預設形參之前
# 預設形參的值在定義階段已經固定死了 m = 10 def func1(x, y, z = m): print(x, y, z) m = 100 func1(1, 2) # 答案依然是10預設形參的值在定義階段已經固定死了
# 預設形參通常應該是不可變型別 # 以下例子的耦合性非常高 m = [] def fun1(x, y, z = m): z.append(x) z.append(y) print(z) fun1(1,2) fun1(3,4) fun1(5,6) print(m) # 結果: # [1, 2] # [1, 2, 3, 4] # [1, 2, 3, 4, 5, 6] # [1, 2, 3, 4, 5, 6]預設形參通常應該是不可變型別
3. 可變長引數
1. 可變長實參 在函式呼叫階段,實參值的個數是不固定的。 2. 可變長形參 在函式定義階段,形參值的個數是不固定的。
可變長形參
* ====》會將溢位的位置實參存到一個元祖中,然後賦值給*後面的形參中
** =====》會將溢位的關鍵字實參以key和value的形式存成字典,然後賦值給**後面的形參中
可變長實參
* ========》會將*後面的值打散成一個個的位置實參,然後與形參一一對應
** ========》會將**後面的值以key=value的形式打散成一個個的關鍵字引數,然後與形參一一對應
(1) 多出來的位置實參賦值給*args
def fun(x, y, *args): # args=(3, 4, 5,6) print(x, y, args) fun(1, 2, 3, 4, 5, 6)# 多出來3,4,5,6四個位置實參
(2)任意數求和:
# 任意數求和 def sum2(*args): # args=(1,2) res = 0 for i in args: res += i return res res = sum2(1, 2,3) print(res)
(3)形參和實參的對應關係
def fun(x,y,*z):# z = (1,2,3,4 ) print(x, y, z) fun(11,22,*[1, 2,3,4]) # fun(11, 22, 1,2,3,4)
(4)*args, **kwargs的應用
# *args,**kwargs可以被當作引數傳遞def index(name, age, sex): print(name, age, sex) def wrapper(*args, **kwargs): # args = ('egon') # kwargs = {'sex': 'male', 'agse': 18} index(*args, **kwargs)# index('egon', sex='male', agse=18) wrapper('egon',sex='male', age='18')
4. 命名關鍵字引數
關鍵字引數: 在*和**之間的定義的引數稱為命名關鍵字引數 關鍵字引數: 命名關鍵字引數必須按照key=value的形式進行傳值
# 在*號後面的形參在實參傳遞的過程中必須是關鍵字引數 def fun(x, *, y, z): print(x, y, z) fun(1, y=2, z=3)
四. 函式物件
函式物件:
函式是第一類物件:函式的記憶體地址可以像一個變數值一樣去使用變數值的使用方法:1. 可以被引用 a = 1 b = a 2. 可以當作引數傳遞3. 可以作為返回值4. 可以當作容器內的元素
1. 函式被引用
def f1(): print('from f1') f = f1 f1 = f f() f1()
2. 可以被當作引數進行傳遞
def f1(func): func() def f2(): print('from f2') f1(f2)
3. 可以作為返回值
def f1(func): return func def f2(): print('from f2') f1(f2)()
4. 作為容器內的元素
作為功能字典使用:
def login(): print('login.....') def register(): print('register.....') func_dict = {'1': login, '2': register} choice = input('>>').strip() if choice in func_dict: func_dict[choice]() else: print('wrong!')
五. 函式巢狀
函式巢狀的兩種型別 1. 函式的巢狀呼叫 2. 函式的巢狀定義
1. 函式的巢狀呼叫
# 函式的巢狀呼叫就是把一個複雜的功能拆分成 # 更小的功能,然後去呼叫它來解決問題 def max2(a, b): if a > b: return a else: return b def max4(a,b,c,d): res = max(a, b) res = max(res,c) res = max(res, d) return res print(max4(1, 2, 3, 4))
2. 函式的巢狀定義
# 從這個例子可以看出來,函式的巢狀宣告可以把一類函式 # 打包到一塊,然後通過傳入一個引數執行不同的函式 from math import pi def circle(banjing, choice): '''choice == 1計算周長,其他的計算面積''' def zhouchang(): return banjing * 2 * pi def mianji(): return pi * banjing ** 2 if choice == '1': res = zhouchang() else: res = mianji() return res
六. 名稱空間
名稱空間
名稱空間就是python的一個專門用來儲存名字和值的記憶體地址對映關係的一個空間。名稱空間的分類 1. 內建名稱空間 2. 全域性名稱空間 3. 本地名稱空間內建名稱空間: 存放的名字:存放的是python直譯器自帶的名字 產生:開啟python直譯器的時候會產生 銷燬:關閉python直譯器的時候會銷燬全域性名稱空間: 存放的名字:存放的是頂格寫入的p名字 產生:在執行python檔案的時候會產生 銷燬:在python檔案執行完畢之後銷燬區域性名稱空間: 存放的名字:存放的是函式內的名字 產生:在執行函式程式碼體的時候會產生 銷燬:在函式執行完畢之後會銷燬名稱空間的載入順序: 內建名稱空間----》全域性名稱空間-------》區域性名稱空間名稱空間的銷燬順序: 區域性名稱空間----》全域性名稱空間-------》內建名稱空間名稱空間的查詢順序: 從當前名稱空間一層一層往上查詢
七. 作用域
作用域: 作用域其實就是把名稱空間分成了兩大類 1. 全域性作用域: 內建名稱空間和全域性名稱空間的名稱 特點: 全域性存活,全域性有效 2. 區域性作用域: 區域性名稱空間的名稱 特點: 臨時存活,區域性有效
作用域關係在函式定義階段已經定義死了,與呼叫的位置無關:
# 例一:作用域關係在定義階段已經定義死了 xxx = 2 def f1(): print(xxx) xxx = 1 # 此時函式執行的時候是會報錯的 # 原因:當定義函式f1的時候發現區域性名稱空間中是有xxx這個變數的 # 因此無論什麼時候呼叫f1函式都會使用區域性名稱空間,因此在呼叫 # f1()函式的時候,執行print(xxx)中的xxx的時候,發現區域性名稱空間 # 目前還沒有這個變數,因此會報錯。 f1()例一:作用域關係在定義階段已經定義死了 例二:作用域關係在定義階段已經定義死了
八. 閉包函式
閉包函式運用的知識: 函式巢狀: 閉包函式就是在一個函式外面包上一層函式 函式物件: 會返回內部函式的記憶體地址 作用域關係:會傳遞值給內部函式進行使用 # 建立了一個閉包函式innter def outter(): x = 1 def innter(): # 函式的巢狀 print('from inner', x) # 函式的作用域關係 return innter # 函式的物件什麼是閉包函式呢? 閉包函式本質上就是一個函式的巢狀。在巢狀的過程中會牽涉到函式的作用域關係。應用廣泛的是裝飾器。