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

相關連接