Python 單雙星號(* & **)解說
Posted on Sep 19, 2022 in Python 程式設計 - 初階 by Amo Chen ‐ 4 min read
Python 內將函數的參數分為 keyword argument 以及 positional argument, 這 2 者的差異在官方文件中有清楚說明,同時這 2 種分別支援單星號 *
與雙星號 **
的用法,是 Python 開發者一定要熟知的用法。
本文將詳細介紹說明。
本文環境
- Python 3.7
Keyword argument
An argument preceded by an identifier (e.g.
name=
) in a function call or passed as a value in a dictionary preceded by**
.
Keyword argument 在呼叫函數/方法時,需要明確指定參數名稱。
例如以下透過 keyword argument 方式呼叫內建的 complex() 類別時(該類別接受 2 個參數,分別是 real
與 imag
),分別明確指定參數值 real=3
, imag=5
:
complex(real=3, imag=5)
除了上述明確指定參數值的方式之外, keyword argument 也支援以 **
2個星號加在字典(dictionary)型態的值前面進行呼叫, Python 就會自動將該字典裡的 key 作為參數名稱, value 作為參數值進行呼叫:
kw = {'real': 3, 'imag': 5}
complex(**kw)
上述範例也等同於下列形式:
complex(**{'real': 3, 'imag': 5})
以 keyword argument 方式呼叫函式/方法,可以無視參數的次序,可讀性也較佳,例如以下範例的結果都是相同的:
complex(real=3, imag=5) # (3+5j)
complex(imag=5, real=3) # (3+5j)
順帶一提, **{'real': 3, 'imag': 5}
的寫法,稱為 unpacking, 可以用來合併多個 dictionary, 例如以下範例:
a = {
'a': 0,
}
ab = {
'a': 1,
'b': 2,
}
c = {
'c': 3,
**a,
**ab,
}
print(c)
# 輸出結果:
# {'c': 3, 'a': 1, 'b': 2}
從上述範例的輸出結果可以發現如果用 unpacking 進行合併,如果有相同的 key 存在,將以後出現的值為準,這也是為何 a: 0
最後會變為 a: 1
的原因。
Positional argument
An argument that is not a keyword argument. Positional arguments can appear at the beginning of an argument list and/or be passed as elements of an iterable preceded by
*
.
Positional argument 與 Keyword argument 不同,不需要明確指定參數名稱,而是以參數值的位置,將參數一對一帶入,例如 complex()
函數定義接受 2 個參數 real
與 imag
:
class complex([real[, imag]])
若以 Positional argument 方式進行呼叫, Python 會自動將參數值 3, 5
按照位置一對一地賦予 real
與 imag
, 所以 real
會得到 3, imag
會得到 5:
complex(3, 5)
所以用 Positional argument 呼叫時的參數值帶入順序相當重要,如果弄錯就可能有 bug 產生,例如以下 2 者結果就會不一樣:
complex(3, 5) # (3+5j)
complex(5, 3) # (5+3j)
Positional argument 支援以 *
1 個星號加在 iterable 型態的值前面進行呼叫,例如:
args = (3, 5)
complex(*args)
上述範例也等同於下列形式:
complex(*(3, 5))
值得注意的是 *
可以用在任何 iterable 型態的值,包含 List, String, Generator 等等,因此 complex(*(3, 5))
也等同於下列形式:
complex(*[3, 5])
另外星號也可以與一般 positional argument 用法混用,例如下列函式:
def func(p1, p2, p3):
print(f"p1={p1}, p2={p2}, p3={p3}")
呼叫時可以部分參數值以 *
代替:
func(1, *[2, 3])
# 輸出結果:
# p1=1, p2=2, p3=3
順帶一提, *[3, 4]
的寫法,也稱為 unpacking, 可以用來 unpacking iterable, 例如以下範例用單星號 unpacking 2 個 list 進行合併:
a = [1, 2]
b = [3, 4]
c = [*a, *b]
print(c)
# 輸出結果:
# [1, 2, 3, 4]
單星號 * 在函式定義上的應用
If the form “*identifier” is present, it is initialized to a tuple receiving any excess positional parameters, defaulting to the empty tuple.
如果想要像 print() 一樣接受不定長度的 positional arguments, 例如:
print(1, 2, 3, 4, 5, 6, 7, 8, 9)
就可以在函式定義使用 *
加上變數名稱, Python 會建立一個 tuple, 然後將所有的 positional 參數值都存進去該 tuple, 例如以下定義的函式 func 以 *args
將 args 變成接受不定長度的參數,最後可以從 func(1, 2, 3)
執行結果發現參數 1, 2, 3 被存進 args
,變成值為 (1, 2, 3)
的 tuple:
def func(*args):
print(args, type(args))
func(1, 2, 3)
# 輸出結果:
# (1, 2, 3) <class 'tuple'>
如果進一步對上述 func
函式帶入 keyword arguments 例如 func(1, 2, 3, a=4)
將會出現以下錯誤:
Traceback (most recent call last):
File "functest.py", line 4, in <module>
func(1, 2, 3, a=4)
TypeError: func() got an unexpected keyword argument 'a'
這是由於 def func(*args)
的寫法代表只接受 positional arguments.
雙星號 ** 在函式定義上的應用
If the form “**identifier” is present, it is initialized to a new ordered mapping receiving any excess keyword arguments, defaulting to a new empty mapping of the same type.
如果想要像 dict() 一樣接受不定長度的 keyword arguments, 例如:
d = dict(a=1, b=2, c=3, d=4, e=5, f=6)
則可以在函式定義使用 **
加上變數名稱, Python 會建立一個 dictionary, 然後將所有的 keyword 參數名稱與參數值都存進去該 dictionary, 例如以下定義的函式 func 以 **kwargs
將 kwargs 變成接受不定長度的 keyword 參數,最後可以從 func(a=1, b=2, c=3)
執行結果發現參數 a=1, b=2, c=3
被存進 kwargs
,變成值為 {'a': 1, 'b': 2, 'c': 3}
的 dictionary:
def func(**kwargs):
print(kwargs, type(kwargs))
func(a=1, b=2, c=3)
# 輸出結果:
# {'a': 1, 'b': 2, 'c': 3} <class 'dict'>
如果進一步對上述 func
函式帶入 positional arguments 例如 func(4, 5, a=1, b=2, c=3)
將會出現以下錯誤:
Traceback (most recent call last):
File "functest.py", line 4, in <module>
func(4, 5, a=1, b=2, c=3)
TypeError: func() takes 0 positional arguments but 2 were given
這是由於 def func(**kwargs)
的寫法代表只接受 keyword arguments.
當然,上述 2 種關於星號的用法也能夠一起使用:
def func(*args, **kwargs):
print(args, type(args))
print(kwargs, type(kwargs))
func(1, 2, 3, a=4, b=5, c=6)
# 輸出結果:
# (1, 2, 3) <class 'tuple'>
# {'a': 4, 'b': 5, 'c': 6} <class 'dict'>
Parameters after “*” or “*identifier” are keyword-only parameters and may only be passed by keyword arguments.
不過單星號 *
一定要在雙星號 **
前面,否則就會出現以下錯誤:
File "functest.py", line 1
def func(**kwargs, *args):
^
SyntaxError: invalid syntax
以上就是關於 Python 單雙星號的介紹, Happy coding!
References
https://docs.python.org/3/glossary.html
https://docs.python.org/3/reference/expressions.html
https://docs.python.org/3/reference/expressions.html#calls
https://docs.python.org/3/faq/programming.html#what-is-the-difference-between-arguments-and-parameters