Python 解包騷操作,看這篇文章

Python 解包騷操作,看這篇文章

來自專欄 Python之禪78 人贊了文章

導讀:本文總結了 Python 解包操作的方方面面,文章略長,看本文前,首先確保身邊有多個不同版本 Python 解釋器的電腦(公眾號回復 conda ,了解如何安裝多個環境),以便隨時驗證代碼。看完記得收藏,方便查閱)

解包在英文里叫做 Unpacking,就是將容器裡面的元素逐個取出來(防杠精:此處描述並不嚴謹,因為容器中的元素並沒有發生改變)放在其它地方,好比你老婆去菜市場買了一袋蘋果回來分別發給家裡的每個成員,這個過程就是解包。Python 中的解包是自動完成的,例如:

>>> a, b, c = [1,2,3]>>> a1>>> b2>>> c3

如果列表中有3個元素,那麼剛好如果列表中有3個元素,那麼剛好可以分配給3個變數。除了列表對象可以解包之外,任何可迭代對象都支持解包,可迭代對象包括元組、字典、集合、字元串、生成器等實現了__next__方法的一切對象。

元組解包

>>> a,b,c = (1,2,3)>>> a1>>> b2>>> c3

字元串解包

>>> a,b,c = "abc">>> aa>>> bb>>> cc

字典解包

>>> a,b,c = {"a":1, "b":2, "c":3}>>> aa>>> bb>>> cc

字典解包後,只會把字典的 key 取出來,value 則丟掉了。

你可能見過多變數賦值操作,例如:

>>> a, b = 1, 2>>> a1>>> b2

本質上也是自動解包過程,等號右邊其實是一個元組對象 (1, 2),有時候我們代碼不小心多了一個逗號 ,,就變成了元組對象

>>> a = 1,>>> a(1,)---------->>> a = 1>>> a1

所以寫代碼的時候需要特別注意。在Python 中,交換兩個變數非常方便,本質上也是自動解包過程。

>>> a, b = 1, 2>>> a, b = b, a>>> a2>>> b1

如果在解包過程中,遇到左邊變數個數小於右邊可迭代對象中元素的個數時該怎麼辦? 好比你們家有3口人,你老婆卻買了4個蘋果,怎麼分配呢?

在Python2中,如果等號左邊變數的個數不等於右邊可迭代對象中元素的個數,是不允許解包的。但在 Python3 可以這麼做了。這個特性可以在 PEP 3132 中看到。

>>> a, b, *c = [1,2,3,4]>>> a1>>> b2>>> c[3, 4]>>>

這種語法就是在某個變數面前加一個星號,而且這個星號可以放在任意變數,每個變數都分配一個元素後,剩下的元素都分配給這個帶星號的變數

>>> a, *b, c = [1,2,3,4]>>> a1>>> b[2, 3]>>> c4

這種語法有什麼好處呢?它使得你的代碼寫起來更簡潔,比如上面例子,在 Python2 中該怎麼操作呢?思考3秒鐘,再看答案。

>>> n = [1,2,3,4]# 使用切片操作>>> a, b, c = n[0], n[1:-1], n[-1]>>> a1>>> b[2, 3]>>> c4

以上是表達式解包的一些操作,接下來介紹函數調用時的解包操作。函數調用時,有時你可能會用到兩個符號:星號*和 雙星號**

>>> def func(a,b,c):... print(a,b,c)...>>> func(1,2,3)1 2 3

func 函數定義了三個位置參數 a,b,c,調用該函數必須傳入三個參數,除此之外,你也可以傳入包含有3個元素的可迭代對象,

>>> func(*[1,2,3])1 2 3>>> func(*(1,2,3))1 2 3>>> func(*"abc")a b c>>> func(*{"a":1,"b":2,"c":3})a b c

函數被調用的時候,使用星號 * 解包一個可迭代對象作為函數的參數。字典對象,可以使用兩個星號,解包之後將作為關鍵字參數傳遞給函數

>>> func(**{"a":1,"b":2,"c":3})1 2 3

看到了嗎?和上面例子的區別是多了一個星號,結果完全不一樣,原因是什麼? 答案是 ** 符號作用的對象是字典對象,它會自動解包成關鍵字參數 key=value 的格式:

>>> func(a=1,b=2,c=3)1 2 3

如果字典對象中的 key 不是 a,b,c,會出現什麼情況?請讀者自行測試。

總結一下,函數調用時,一個星號可作用於所有的可迭代對象,稱為迭代器解包操作,作為位置參數傳遞給函數,兩個星號只能作用於字典對象,稱之為字典解包操作,作為關鍵字參數傳遞給函數。使用 *** 的解包的好處是能節省代碼量,使得代碼看起來更優雅,不然你得這樣寫:

>>> d = {"a":1, "b":2, "c":3}>>> func(a = d[a], b=d[b], c=d[c])1 2 3>>>

到這裡,解包還沒介紹完,因為 Python3.5,也就是 PEP 448 對解包操作做了進一步的擴展, 在 3.5 之前的版本,函數調用時,一個函數中解包操作只允許一個* 和 一個**。從 3.5 開始,在函數調用中,可以有任意多個解包操作,例如:

# Python 3.4 中 print 函數 不允許多個 * 操作>>> print(*[1,2,3], *[3,4]) File "<stdin>", line 1 print(*[1,2,3], *[3,4]) ^SyntaxError: invalid syntax>>>

再來看看 python3.5以上版本

# 可以使用任意多個解包操作>>> print(*[1], *[2], 3)1 2 3

從 3.5 開始可以接受多個解包,於此同時,解包操作除了用在函數調用,還可以作用在表達式中。

>>> *range(4), 4(0, 1, 2, 3, 4)>>> [*range(4), 4][0, 1, 2, 3, 4]>>> {*range(4), 4}{0, 1, 2, 3, 4}>>> {x: 1, **{y: 2}}{x: 1, y: 2}

新的語法使得我們的代碼更加優雅了,例如拼接兩個列表可以這樣:

>>> list1 = [1,2,3]>>> list2 = range(3,6)>>> [*list1, *list2][1, 2, 3, 3, 4, 5]>>>

可不可以直接用 + 操作呢?不行,因為 list 類型無法與 range 對象 相加,你必須先將 list2 強制轉換為 list 對象才能做 +操作,這個留給讀者自行驗證。

再來看一個例子:如何優雅的合併兩個字典

>>> a = {"a":1, "b":2}>>> b = {"c":3, "d":4}>>> {**a, **b}{a: 1, b: 2, c: 3, d: 4}

在3.5之前的版本,你不得不寫更多的代碼:

>>> import copy>>>>>> c = copy.deepcopy(a)>>> c.update(b)>>> c{a: 1, b: 2, c: 3, d: 4}

最後給你總結一下:

  1. 自動解包支持一切可迭代對象
  2. python3中,開始支持更高級的解包操作,用星號操作使得等號左邊的變數個數可以少於右邊迭代對象中元素的個數。
  3. 函數調用時,可以用 * 或者 ** 解包可迭代對象
  4. python3.5,函數調用和表達式中可支持更多的解包參數。

公眾號:Python之禪


推薦閱讀:

TAG:Python入門 | Python |