今天來介紹我常常搞不清楚的 *args
先來回憶一下我們昨天學到的 iterable unpacking:
a, b, *c = 10, 20, 'a', 'b'
print(a, b)
10 20
print(c)
['a', 'b']
可以看到 c 將 'a', 'b' 兩個元素 unpacking 在陣列中。
我們可以利用同樣的概念,放在 funciton 的引數中:
def func1(a, b, *args):
print(a)
print(b)
print(args)
func1(1, 2, 'a', 'b')
1
2
('a', 'b')
這裡有幾件事值得注意:
與 iterable unpacking 不同, *args 會是 tuple 而不是 list.
參數名稱 args 是一種傳統命名,實際上你可以取名 *a, *b, ...,只是別人大概會看不懂。
在 *args 參數之後你就不能再使用位置引數——用下去會有其他效果,我們明天解釋。
def func1(a, b, *my_vars):
print(a)
print(b)
print(my_vars)
func1(10, 20, 'a', 'b', 'c')
10
20
('a', 'b', 'c')
def func1(a, b, *c, d):
print(a)
print(b)
print(c)
print(d)
func1(10, 20, 'a', 'b', 100)
---------------------------------------------------------------------------
TypeError Traceback (most recent call last)
/Users/maotingyang/Downloads/star-args.ipynb Cell 14 in <cell line: 1>()
----> <a href='vscode-notebook-cell:/Users/maotingyang/Downloads/star-args.ipynb#X16sZmlsZQ%3D%3D?line=0'>1</a> func1(10, 20, 'a', 'b', 100)
TypeError: func1() missing 1 required keyword-only argument: 'd'
我們來設計一個計算平均數的 function:
def avg(*args):
count = len(args)
total = sum(args)
return total/count
avg(2, 2, 4, 4)
3.0
But watch what happens here:
avg()
---------------------------------------------------------------------------
ZeroDivisionError Traceback (most recent call last)
/Users/maotingyang/Downloads/star-args.ipynb Cell 19 in <cell line: 1>()
----> <a href='vscode-notebook-cell:/Users/maotingyang/Downloads/star-args.ipynb#X24sZmlsZQ%3D%3D?line=0'>1</a> avg()
/Users/maotingyang/Downloads/star-args.ipynb Cell 19 in avg(*args)
<a href='vscode-notebook-cell:/Users/maotingyang/Downloads/star-args.ipynb#X24sZmlsZQ%3D%3D?line=1'>2</a> count = len(args)
<a href='vscode-notebook-cell:/Users/maotingyang/Downloads/star-args.ipynb#X24sZmlsZQ%3D%3D?line=2'>3</a> total = sum(args)
----> <a href='vscode-notebook-cell:/Users/maotingyang/Downloads/star-args.ipynb#X24sZmlsZQ%3D%3D?line=3'>4</a> return total/count
ZeroDivisionError: division by zero
因為我們沒傳任何引數進去,出現分母為零的錯誤。
可以用兩種方式修改:
def avg(*args):
count = len(args)
total = sum(args)
if count == 0:
return 0
else:
return total/count
avg(2, 2, 4, 4)
3.0
avg()
0
上面是一種解法。
另一種解法是我們設定一個必填的參數 a,所以沒傳入任何引數時就會出錯:
def avg(a, *args):
count = len(args) + 1
total = a + sum(args)
return total/count
avg(2, 2, 4, 4)
3.0
avg()
---------------------------------------------------------------------------
TypeError Traceback (most recent call last)
/Users/maotingyang/Downloads/star-args.ipynb Cell 27 in <cell line: 1>()
----> <a href='vscode-notebook-cell:/Users/maotingyang/Downloads/star-args.ipynb#X35sZmlsZQ%3D%3D?line=0'>1</a> avg()
TypeError: avg() missing 1 required positional argument: 'a'
我覺得第二種方法比較聰明,你呢?
def func1(a, b, c):
print(a)
print(b)
print(c)
l = [10, 20, 30]
下面這種 unpacking 方式會失敗:
func1(l)
---------------------------------------------------------------------------
TypeError Traceback (most recent call last)
/Users/maotingyang/Downloads/star-args.ipynb Cell 33 in <cell line: 1>()
----> <a href='vscode-notebook-cell:/Users/maotingyang/Downloads/star-args.ipynb#X44sZmlsZQ%3D%3D?line=0'>1</a> func1(l)
TypeError: func1() missing 2 required positional arguments: 'b' and 'c'
Python 把 l 陣列當作第一個引數,少了剩餘的引數而出錯。
我們可以利用星號 unpacking :
*l,
(10, 20, 30)
func1(*l)
10
20
30
話說混用位置引數和關鍵字引數會如何呢?
def func1(a, b, c, *d):
print(a)
print(b)
print(c)
print(d)
func1(10, c=20, b=10, 'a', 'b')
Input In [22]
func1(10, c=20, b=10, 'a', 'b')
^
SyntaxError: positional argument follows keyword argument
記得之前學到的嗎?一旦在函式傳入關鍵字引數,後面就不能再傳入位置引數了。
明天我們會學到如何解決這個問題,我們明天見!
參考:Python 3: Deep Dive (Part 1 - Functional)