python:函式中五花八門的引數形式(茴香豆的『回』字有四種寫法)
毫不誇張的說,python語言中關於函式引數的使用,是我見過最為靈活的,隨便怎麼玩都可以,本文以數學乘法為例,演示幾種不同的傳參形式:
一、預設引數
def multiply1(x, y): return x * y print("multiply1:", multiply1(2, 3))
輸出 multiply1: 6 ,這是最平淡無奇的函式寫法。
python支援預設引數,比如:如果想讓1個數自動乘10,又不想定義新的函式,上面的程式碼,可以改成:
def multiply1(x, y=10): return x * y print("multiply1:", multiply1(2, 3)) print("multiply1:", multiply1(2))
輸出
multiply1: 6
multiply1: 20
另外,呼叫函式時,除了按順序依次給值外,還可以“顯式”的命名傳值,示例如下:
print("multiply1:", multiply1(x=2, y=4)) print("multiply1:", multiply1(y=3, x=5)) print("multiply1:", multiply1(x=4))
輸出:
multiply1: 8
multiply1: 15
multiply1: 40
顯式指定引數名稱後,順序就不重要了。
二、可變引數(tuple)
如果乘數多於2個,上面的版本顯然不能滿足要求,可以參考下面的版本:
def multiply2(*numbers): result = 1 for i in numbers: result *= i return result print("multiply2(1):", multiply2(1, 2, 3)) test = (1, 2, 3) print("multiply2(2):", multiply2(*test)) print("multiply2(3):", multiply2(test))
引數前加一個*,就表示引數個數不固定(其實python是當作tuple來看待),上面的程式碼會輸出:
multiply2(1): 6
multiply2(2): 6
multiply2(3): (1, 2, 3) #注意這一行,如果是一個已經定義好的tuple變數,想傳入可變引數中呼叫,必須前加*,否則的話,程式不報錯,但不是你期望的結果。
當可變引數與預設引數結合在一起時,有一個地方需要注意:
def multiply2_2(*numbers, base=10): result = 1 for i in numbers: result *= i return result * base print("multiply2_2:", multiply2_2(1, 2, 3, base=10))
輸出:
multiply2_2: 60
這很好理解,但如果在前面再加一個預設引數:
def multiply2_3(x=2, *numbers, base=10): print("x=", x, ",numbers:", numbers, ",base:", base) result = 1 for i in numbers: result *= i return x * result * base print("multiply2_3(1):", multiply2_3(4, 1, 2, base=10)) print("multiply2_3(2):", multiply2_3(4, *(1, 2), base=10)) print("multiply2_3(3):", multiply2_3(*(1, 2), base=10)) # print("multiply2_3(4):", multiply2_3(x=4, *(1, 2), base=10)) # 這裡會報錯
輸出:
x= 4 ,numbers: (1, 2) ,base: 10
multiply2_3(1): 80
x= 4 ,numbers: (1, 2) ,base: 10
multiply2_3(2): 80
x= 1 ,numbers: (2,) ,base: 10
multiply2_3(3): 20
注意:multiply2_3(*(1, 2), base=10) 這行呼叫時,(1,2)這個tuple裡的第1個元素實際上是給到x了,從輸出就能印證,函式定義中的x=2預設值,並沒有起到作用。
如果把最後一行 print("multiply2_3(4):", multiply2_3(x=4, *(1, 2), base=10)) 的註釋去掉,執行會報錯:
print("multiply2_3(4):", multiply2_3(x=4, *(1, 2), base=10)) # 這裡會報錯
TypeError: multiply2_3() got multiple values for argument 'x'
原因在於(1,2)的第1個元素會賦值給x,然後又指定了x=4,所以python會認為引數x有2個值1、4,不知道該用哪個,只好蒙逼報錯。
三、字典引數(dic)
如果在引數前加2個*,就變成字典(key-value)引數了,參考下面的示例:
def multiply3(**numbers): result = 1 for a in numbers: result *= numbers[a] return result dic1 = {"a": 1, "b": 2, "c": 3} print("multiply3(1):", multiply3(**dic1)) print("multiply3(2):", multiply3(**{"a": 1, "b": 2, "c": 3})) print("multiply3(3):", multiply3(a=1, b=2, c=3))
注意呼叫的姿勢,有好幾種姿勢可解鎖~_^ 輸出如下:
multiply3(1): 6
multiply3(2): 6
multiply3(3): 6
可能有同學感覺dic引數太靈活,單從呼叫引數的“長相”上,幾乎就是一個json串,隨便傳,太浪了! 有沒有什麼辦法約束一下,比如:要求dic字典中,只能包含指定的key。辦法當然有:
def multiply4(*, a, b): return a * b print("multiply4:", multiply4(**{"a": 2, "b": 3})) print("multiply4:", multiply4(a=2, b=3)) print(multiply4(**{"a": 2, "b": 3, "c": 1}))
注意寫法:*,a,b 表示該函式接受dic字典引數,但是dic中的key名稱,只能是"a" , "b",輸出:
multiply4: 6
multiply4: 6
Traceback (most recent call last):
File ".../03.py", line 42, in <module>
print(multiply4(**{"a": 2, "b": 3, "c": 1}))
TypeError: multiply4() got an unexpected keyword argument 'c'
即:最後一行呼叫print(multiply4(**{"a": 2, "b": 3, "c": 1}))時,這裡出現了一個不守約定的key名:c ,所以報錯了
當然,也可以配合預設引數,做些變化,比如:想給乘法結果設定一個基數。
def multiply5(base, *, a=1, b): return base * a * b print("multiply5(1):", multiply5(10, **{"a": 2, "b": 3})) print("multiply5(2):", multiply5(10, b=3))
輸出:
multiply5(1): 60
multiply5(2): 30
四、大亂燉
如果把上面的各種奇淫技巧結合起來,就蠻頭暈了,強烈不推薦!
def multiply6(a, b, c=1, *d, e=1, f): print("a=", a, ",b=", b, ",c=", c, ",d=", d, ",e=", e, ",f=", f) result = a * b * c for i in d: result *= i result *= (e * f) return result print("multiply6(1):", multiply6(1, 2, (3, 4, 5), f=6)) print("multiply6(2):", multiply6(1, 2, *(3, 4, 5), f=6)) print("multiply6(3):", multiply6(1, 2, 3, *(4, 5, 6), e=7, f=8))
輸出:
a= 1 ,b= 2 ,c= (3, 4, 5) ,d= () ,e= 1 ,f= 6
multiply6(1): (3, 4, 5, 3, 4, 5, 3, 4, 5, 3, 4, 5, 3, 4, 5, 3, 4, 5, 3, 4, 5, 3, 4, 5, 3, 4, 5, 3, 4, 5, 3, 4, 5, 3, 4, 5)
a= 1 ,b= 2 ,c= 3 ,d= (4, 5) ,e= 1 ,f= 6
multiply6(2): 720
a= 1 ,b= 2 ,c= 3 ,d= (4, 5, 6) ,e= 7 ,f= 8
multiply6(3): 40320
tips: 首次呼叫故意少寫了一個*,於是(3,4,5)這個元組就被乘了2*6次,變成了3,4,5連續重複12次。
def multiply7(a, b=10, *c, **d): print("a=", a, ",b=", b, ",c=", c, ",d=", d) result = a * b for i in c: result *= i for j in d: result *= d[j] return result print("multiply7(1):", multiply7(2, *(3, 4), **{"x": 5, "y": 6})) print("multiply7(2):", multiply7(2, *(3, 4), x=5, y=6)) print("multiply7(3):", multiply7(2, 3, 4, x=5, y=6)) print("multiply7(4):", multiply7(2, 3, 4, 5, 6))
輸出:
a= 2 ,b= 3 ,c= (4,) ,d= {'x': 5, 'y': 6}
multiply7(1): 720
a= 2 ,b= 3 ,c= (4,) ,d= {'x': 5, 'y': 6}
multiply7(2): 720
a= 2 ,b= 3 ,c= (4,) ,d= {'x': 5, 'y': 6}
multiply7(3): 720
a= 2 ,b= 3 ,c= (4, 5, 6) ,d= {}
multiply7(4): 720
雖然看上去,呼叫方式各式各樣,但是結果全是720,而且函式定義中的b=10,這個預設值完全不起作用。
五、多返回值
前面提到的都是入參的各種姿勢,在函式返回結果上,python也有過人之處,可以一次返回多個元素:
def swap(x, y): return y, x a, b = swap(1, 2) print(a, b) result = swap(1, 2) print(type(result), result)
輸出:
2 1
<class 'tuple'> (2, 1)
tips: 多個返回值,只是假象,其實返回的是一個tuple物件。
參考文件: