Python 函數的「一顆星*」與「兩顆星**」 - tsungjung411/python-study GitHub Wiki
名詞解釋
- 「一顆星*」: single star/asterisk
- 「兩顆星**」: double stars/asterisks, two stars/asterisks
- 「一顆星*」與「兩顆星**」是 python 函數的特殊用法,用來支援「不定個數(arbitrary number)」的函數參數
- 「一顆星*」是給 list/tuple 使用
- 「兩顆星**」是給 dict 使用,可以想像成 key:value 會是 *:*,所以需要兩個星 :star: :star:
來看看「一顆星*」的實際範例
- 「一顆星*」若用在函數的參數(parameter),表示「收合(Collapse)」多個傳入引數,變成一個傳入參數
- 「一顆星*」若用在函數的引數(argument),表示「展開(Expand)」一個傳入引數,變成多個傳入參數
class Math:
def sum(self, *input_x):
# *input_x 表示「收合(Collapse)」多個傳入參數,變成一個參數
print('Math: sum: type(input_x):', type(input_x))
total = 0
index = 0
for x in input_x:
print(' - input_x[{}] = {} (type:{})'.format(index, x, type(x)))
index += 1
total += x
# end-of-for
return total
# end-of-def
def avg(self, *input_x):
print('Math: avg: type(input_x):', type(input_x))
# *input_x: 表示「展開(Expand)」一個參數,變成多個參數
return self.sum(*input_x) / len(input_x)
# end-of-def
def avg_error(self, *input_x):
print('Math: avg_error: type(input_x):', type(input_x))
# [錯誤使用]
# 以為 avg(self, *input_x) 的 input_x
# 等同 sum(self, *input_x) 的 input_x
return self.sum(input_x) / len(input_x)
# end-of-def
# end-of-class
math = Math()
print("sum(1,2,3): ", math.sum(1,2,3))
print()
print("avg(1,2,3): ", math.avg(1,2,3))
print()
print("avg_error(1,2,3): ", math.avg_error(1,2,3))
執行結果:
Math: sum: type(input_x): <class 'tuple'>
- input_x[0] = 1 (type:<class 'int'>)
- input_x[1] = 2 (type:<class 'int'>)
- input_x[2] = 3 (type:<class 'int'>)
sum(1,2,3): 6
Math: avg: type(input_x): <class 'tuple'>
Math: sum: type(input_x): <class 'tuple'>
- input_x[0] = 1 (type:<class 'int'>)
- input_x[1] = 2 (type:<class 'int'>)
- input_x[2] = 3 (type:<class 'int'>)
avg(1,2,3): 2.0
Math: avg_error: type(input_x): <class 'tuple'>
Math: sum: type(input_x): <class 'tuple'>
- input_x[0] = (1, 2, 3) (type:<class 'tuple'>)
---------------------------------------------------------------------------
TypeError Traceback (most recent call last)
...
<ipython-input-32-5724cdbc9f59> in sum(self, *input_x)
9 index += 1
10
---> 11 total += x
12 # end-of-for
13 return total
TypeError: unsupported operand type(s) for +=: 'int' and 'tuple'
來看看「兩顆星**」的實際範例
- 「兩顆星**」核心概念,都如同「一顆星*」
- 「兩顆星**」若用在函數的參數(parameter),表示「收合(Collapse)」多個傳入引數,變成一個傳入參數
- 「兩顆星**」若用在函數的引數(argument),表示「展開(Expand)」一個傳入引數,變成多個傳入參數
展開例子1
x = {'A': '1', 'B': 2, 'C': 3.0, 'D': True}
def expand(A, B, C, D, E=None):
print(A, B, C, D, E)
print(x)
expand(**x)
執行結果:
{'A': '1', 'B': 2, 'C': 3.0, 'D': True}
1 2 3.0 True None
- 必要的參數,必須一致,否則會有錯誤:
- 數量太少
TypeError: expand() missing 1 required positional argument: 'D'
- 數量太多
TypeError: expand() got an unexpected keyword argument 'F'
- 數量太少
收合例子1
def collapse(**args):
print('args:', args)
collapse(A='1', B=2, C=3.0, D=True)
執行結果:
args: {'A': '1', 'B': 2, 'C': 3.0, 'D': True}
展開與收合並用
def collapse(**args):
print('args:', args)
collapse(A='1', B=2, C=3.0, D=True)
x = {'A': '1', 'B': 2, 'C': 3.0, 'D': True}
print('x:', x)
collapse(**x)
執行結果:
args: {'A': '1', 'B': 2, 'C': 3.0, 'D': True}
x: {'A': '1', 'B': 2, 'C': 3.0, 'D': True}
args: {'A': '1', 'B': 2, 'C': 3.0, 'D': True}
Misc
def black_box(**args_dict):
# *args_dict 表示「收合(Collapse)」多個傳入參數,變成一個參數
# *args_dict 一般常命名成 *kwargs,表示 keyword arguments
print('black_box: type(args_dict):', type(args_dict))
for key, value in args_dict.items():
print(' - args_dict[{}] = {}'.format(key, value))
# end-of-for
print('x =', args_dict['x'])
print('y =', args_dict.get('y'))
return args_dict
# end-of-def
def forward(**args_dict):
return black_box(**args_dict)
# end-of-def
print("black_box(x=1,y=2): ", black_box(x=1,y=2))
print()
print("forward(x=1,y=2,z=3,w=4): ", forward(x=1,y=2,z=3,w=4))
執行結果:
black_box: type(args_dict): <class 'dict'>
- args_dict[x] = 1
- args_dict[y] = 2
x = 1
y = 2
black_box(x=1,y=2): {'x': 1, 'y': 2}
black_box: type(args_dict): <class 'dict'>
- args_dict[z] = 3
- args_dict[w] = 4
- args_dict[x] = 1
- args_dict[y] = 2
x = 1
y = 2
forward(x=1,y=2,z=3,w=4): {'z': 3, 'w': 4, 'x': 1, 'y': 2}
補充說明:
- 「兩顆星**」用在支援 python 函數的「多載(overloading)」功能
- 多載(overloading):相同函數名稱,但有不同的參數型別、不同的參數個數
- 實際上,Python 並無「多載(overloading)」功能,而是透過「兩顆星**」來達到一樣的目的
- Python 只有一個函數統一窗口,不像 Java 那樣有多個函數窗口
關於「一顆星*」的各種 API 銜接範例
class Math:
def sum(*x): # 將帶進入的引數收合起來,成為 x (tuple)
total = 0
for xx in x:
total += xx
return total
# end-of-def
# end-of-class
print("Math.sum(1,2,3,4,5) = ", Math.sum(1,2,3,4,5))
x_list = [1,2,3,4,5] # 型別是 list
print("Math.sum(*x_list) = ", Math.sum(*x_list)) # x_list 需要展開
def get_x():
return 1,2,3,4,5 # 回傳值型別是 tuple
# end-of-def
print("Math.sum(*get_x()) = ", Math.sum(*get_x())) # get_x() 的回傳值,需要展開
執行結果:
Math.sum(1,2,3,4,5) = 15
Math.sum(*x_list) = 15
Math.sum(*get_x()) = 15