函數的5種參數類型
POSITIONAL_OR_KEYWORD(位置參數或關鍵字參數)
VAR_POSITIONAL(可變參數)
KEYWORD_ONLY(關鍵字參數)
VAR_KEYWORD(可變關鍵字參數)
POSITIONAL_ONLY(位置參數)
POSITIONAL_OR_KEYWORD如其名所見,既可以用位置傳參,也可以用關鍵字傳參
def foo(name):
print(name)
foo('a') # a
foo(name='a') # a
VAR_POSITIONAL是可變參數,通過*來聲明,它會把接收到的值存入一個元組
def foo(*args):
print(args)
foo(1, 2, 3) # (1,2,3)
KEYWORD_ONLY只能通過關鍵字傳參,這種參數會在VAR_POSITIONAL參數類型的後面,只能通過指定關鍵字來傳參,不可以用位置傳參
def f(name, *, val):
print(name, val)
f('a', val=1) # a 1
VAR_KEYWORD是可變關鍵字參數,通過前綴**來聲明,這種參數類型可以接收0個或多個參數,並存入一個字典
def foo(**kw):
for key, value in kw.items():
print('%s=%s' % (key, value), end=' ')
foo(a=1, b=2, c=3) # a=1 b=2 c=3
高版本的Python無法創建一個POSITIONAL_ONLY類型的參數,但是有些使用C語言實現且不接收關鍵字參數的函數(如divmod)支持
默認參數
- VAR系列的兩個參數(可變參數、可變的關鍵詞參數)不允許設置默認參數,而另外兩個參數(位置和關鍵詞參數、關鍵詞參數)可以設置默認參數。 因爲VAR_POSITIONAL的默認參數是tuple()空元祖,而VAR_KEYWORD的默認參數是dict()空字典。
- 默認參數的位置POSITIONAL_OR_KEYWORD類型的默認參數一定要放在後面,否則會報錯。
- KEYWORD_ONLY雖然沒有強制要求,因爲都是用關鍵字傳參,誰先誰後無所謂,但最好還是放在後面。
- 默認參數最好不要設置爲可變類型(如dict、list、set)。 如果把默認參數設置爲可變類型,並在函數中改變了該參數的值,下次再調用它就不再是默認值了。
return
return作用:
- 結束函數
- 返回對象
不寫return語句會默認返回None
高階函數
- python一切皆對象,函數也是對象。
- 函數本身可以賦值給變量,即變量可以指向函數對象。
- 函數名其實就是指向函數對象的變量。
- 因爲變量可以指向函數對象,函數的參數能接收變量,那麼一個函數就可以接收另一個函數作爲參數,這種函數稱之爲高階函數。
- 函數對象也可以作爲函數的返回值。
- 內置的高階函數有filter,map,reduce。
匿名函數
python 使用 lambda 來創建匿名函數。
- lambda只是一個表達式,函數體比def簡單很多。
- lambda的主體是一個表達式,而不是一個代碼塊。僅僅能在lambda表達式中封裝有限的邏輯進去。
- lambda函數擁有自己的命名空間,且不能訪問自有參數列表之外或全局命名空間裏的參數。
- 雖然lambda函數看起來只能寫一行,卻不等同於C或C++的內聯函數,後者的目的是調用小函數時不佔用棧內存從而增加運行效率。
語法
lambda [arg1 [,arg2,.....argn]]:expression
變量作用域
L:local 函數內部作用域
E:enclosing 外部嵌套函數的作用域
G:global 全局作用域
B:build-in 內置作用域
要修改global作用域中變量的值,要用global關鍵字對變量進行聲明
num = 1
def foo():
global num # 需要使用 global 關鍵字聲明
print(num) # 1
num = 123
print(num) # 123
foo()
print(num) # 123
內部函數要修改外部函數的變量的值,用nonlocal關鍵字聲明變量
def outer():
num = 10
def inner():
nonlocal num # nonlocal關鍵字聲明
num = 100
print(num) # 100
inner()
print(num) # 100
outer()
遞歸
遞歸條件:
- 需要調用自身函數
- 要有遞歸出口,也就是結束條件
遞歸調用實際上是函數自己在調用自己,而函數的調用開銷是很大的,系統要爲每次函數調用分配存儲空間,並將調用點壓棧予以記錄。而在函數調用結束後,還要釋放空間,彈棧恢復斷點。所以說,函數調用不僅浪費空間,還浪費時間。同一個問題,如果遞歸解決方案的複雜度不明顯優於其它解決方案的話,那麼使用遞歸是不划算的。方式有些問題使用迭代算法是很難甚至無法解決的(比如漢諾塔問題)。這時遞歸的作用就顯示出來了。