Python中的星號:用途及使用方法
原作者:Trey Hunner
Python中 *和**很常見。無論是對於編程萌新還是許多從其他編程語言(可能無與之完全等效的運算符)遷移過來的的人,這兩個運算符有時可能有點兒神秘。在此,我想詳述這些運算符的用途及其多種使用方式。
這些年來,*和**運算符的本領越來越大,我將討論當前這些運算符的所有使用方式,並標註哪些用途僅適用於Python的現代版本。所以,如果您在Python 2時代就學會了*和**,我還是建議您至少瀏覽一下這篇文章,因為Python 3為這些運算符添加了很多新用途。
如果您初學Python,還不熟悉關鍵字參數(又名命名參數),那麼我建議先閱讀我的關於Python關鍵字參數的文章。
不在本文詳述範圍內的內容:
本文所述的*和**,指的是*和**前綴運算符,而不是中綴運算符。
所以指的不是乘法和乘冪:
本文所述之內容:
我們講的是*和**前綴運算符,即在變數前面使用的*和**運算符。例如:
這包括:
使用*和**將參數傳遞給函數
使用*和**捕捉傳遞到函數中的參數
使用*接受強制關鍵字參數
使用*在元組拆包封期間時捕獲各項
使用*將迭代器解解包到列表/元組中
用**把詞典解包到其他詞典
即便您認為您熟悉所有這些*和**的使用方法,我仍建議您查看下面的每個代碼塊,以確保它們都是您熟悉的。在過去的幾年中,Python核心開發人員一直在為這些運算符添加新功能,*和**的一些新用途很容易被忽略。
星號在函數調用中解包參數
調用函數時,使用*運算符可將迭代對象解包到不同參數中:
print(*fruits)將fruits列表中的所有項作為單獨的參數傳遞到print函數調用中,我們甚至不需要知道列表包含多少個參數。
*運算符不僅僅是語法糖。如果沒有*,除非列表是固定長度的,否則無法做到將特定迭代對象中的所有項作為單獨的參數提交。
下面是另一個例子:
在這裡,我們接受以列表為元素的列表,並返回 「轉置」後的列表:
**運算符執行類似的操作,但是使用關鍵字參數。**操作符允許我們取一個鍵值對字典,並將其在函數調用中解包成關鍵字參數:
根據我的經驗,**用於將關鍵字參數解包到函數調用中並不常見。我看到的最常見的地方是執行繼承:調用super通常會用到*和**。
截至Python3.5,函數調用中*和**均可被多次使用。
使用*多次有時也挺方便的:
使用**多次與之類似:
但是,當使用**多次時,需要小心。Python中的函數不能多次指定相同的關鍵字參數,因為每個字典中與**一起使用的鍵必須是不同的,否則將拋出異常。
星號用於打包函數中的參數
*運算符在定義函數時,用於收集所有的位置參數到一個新的元組:
Python的print和zip函數接受任意數量的位置參數。這個運用*的參數打包方法允許我們構造同print和zip相類似的接受任意數量參數的函數。
**運算符還有另一面:在定義函數時,可以使用**將賦予該函數的任何關鍵字參數捕捉到字典中:
**將捕捉我們賦予這個函數的任何關鍵字參數,並將其放入一個字典中,該字典將引用attributes參數。
同時使用位置參數與強制關鍵字參數時
自Python 3始,我們現有一個特殊的語法來接受強制關鍵字參數。強制關鍵字參數是只能使用關鍵字語法指定的函數參數,這意味著它們不能依據相對位置指定。
若要接受強制關鍵字參數,可在定義函數時於*後放置命名參數:
上面的函數可以這樣使用:
參數dictionary和default在*keys之後出現,這意味著它們只能被指定為關鍵字參數。如果我們試圖依據位置指定它們,我們將收到報錯:
Python中的此種情況將在 PEP 3102詳述
無位置參數只有強制關鍵字參數時
強制關鍵字參數特性很酷,但若想在不捕獲過多的無限位置參數的情況下獲取強制關鍵字參數,該如何操作?
Python語法里允許用一個略怪異的 * 實現它:
這個函數接受iterable參數,該參數可以按相對位置被指定(作為第一個參數),或者依據名稱指定,而fillvalue參數是強制關鍵字參數。這意味著我們可以這樣調用with_previous:
但不是這樣:
此函數接受兩個參數,其一fillvalue必須被指定為關鍵字參數。
我在捕捉任意數量的位置參數時通常使用強制關鍵字參數,但有時還是會使用*來強制參數僅通過位置指定。
Python內置的sorted函數實際上使用了這種方法。如果查看sorted的幫助信息,您將看到以下內容:
此例有一個單獨的*,在sorted 的文檔化的參數中。
星號用於元組解包
Python 3還新增了一種使用*運算符的方法,它只與前文所述的函數定義與函數調用時*表現出的特性有關。
如今 * 運算符也可以用於元組解包:
如果您想知道「自己的代碼中可以在哪裡使用它」,請看一下我所著關於Python中的元組解包的文章中的示例。在這篇文章中,我展示了使用*運算符替代完成序列切片的一些場景。
通常,當我講授 * 時,總強調只能在一個簡單的多重賦值調用中使用一種 *表達。這在技術上是不正確的,因為在嵌套形式的解包中可以使用兩種(我在元組解包文章中會談到嵌套形式的解包):
不過我還沒找到這個的好例子,即使你找到了,我也不建議使用它,因為它看起來有點晦澀。
Python 3中PEP是PEP 3132 加入了這個,並不長。
未完,下篇請看今日推送的第二篇文字
英文原文:http://treyhunner.com/2018/10/asterisks-in-python-what-they-are-and-how-to-use-them/
譯者:盈韜
※IPython 7.0發布:Async REPL
※Python 的後Guido時代
TAG:Python部落 |