python中的*和**引數傳遞機制
python的引數傳遞機制具有值傳遞(int、float等值資料型別)和引用傳遞(以字典、列表等非值物件資料型別為代表)兩種基本機制以及方便的關鍵字傳遞特性(直接使用函式的形參名指定實參的傳遞目標,如函式定義為def f(a,b,c)
,那麼在呼叫時可以採用f(b=1,c=2,a=3)
的指定形參目標的傳遞方式,而不必拘泥於c語言之類的形參和實參按位置對應)
除此之外,python中還允許包裹方式的引數傳遞,這未不確定引數個數和引數型別的函式呼叫提供了基礎:
def f(*a,**b)
包裹引數傳遞的實現是在定義函式時在形參前面加上*
或**
,*
所對應的形參(如上面的a)會被解釋為一個元組(tuple,而**
def f(*a,**b):
print(a)
print(b)
a=3
b=4
f(a,b,m=1,n=2)
上面程式碼的執行結果是:
(3, 4)
{'n': 2, 'm': 1}
可見,對於不使用關鍵字傳遞的變數,會被作為元組的一部分傳遞給*a
,而使用關鍵字傳遞的變數作為字典的一部分傳遞給了**b
。
同時有個tricky的地方,python中規定非關鍵字傳遞的變數必須寫在關鍵字傳遞變數的前面,所以混合使用*
和**
時肯定時*
形參在**
形參的前面。
此外,在進行函式呼叫時,與之配套的就有個被稱為解包裹的方式:
def f(*a,**b):
print(a)
print(b)
c=3
d=4
h=(c,d)
k={"m":1,"n":2}
f(*h,**k)
上面程式碼的輸出與前面一致。
把元組或字典作為引數傳入時,如果要適配包裹形式的形參定義(如上面將h傳給*a
,k傳給**b
),按照元組用*
,字典用**
的方式“解包裹”傳遞即可。
實際上,在呼叫f時使用*
,是為了提醒Python:我想要把實參h拆成分散的2個元素c和d,進行分別傳遞(所有上面程式碼中的f定義成def f(arg1,arg2,**b)
也是可以的,這樣arg1會獲得3這個值而arg2會獲得4這個值)。**
同理類似。另外,解包裹時*
def f(c,d,n,m):
print(c)
print(d)
print(m)
print(n)
a=3
b=4
h=[a,b]
k={"m":1,"n":2}
f(*h,**k)
上面的輸出是
3
4
1
2
與前面所述相符,因此要注意,對於**k
這種字典的解包裹,要求函式的形參名和字典中的key值對應,
上面的例子中如果把def f(c,d,n,m)
中的n改為其它的字母就會報錯
有了包裹傳遞後,呼叫函式時就可以傳遞任意數量的引數,而由於元組和字典都是有__len__
方法可以獲得其元素個數的,所以在編寫函式時可根據這一資訊對不同的引數數目進行不同處理。