歡迎光臨
每天分享高質量文章

5分鐘,關於Python 解包,你需要知道的一切

來自:Python之禪(微信號:VTtalk)


導讀:本文總結了 Python 解包操作的方方面面,文章略長,看本文時,身邊最好有多個不同版本 Python 解釋器的電腦,以便隨時驗證代碼。如果你正在公交地鐵上,看完後請先收藏,方便查閱。

本文閱讀時間大約:5分鐘

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

>>> a, b, c = [1,2,3]
>>> a
1
>>> b
2
>>> c
3

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

元組解包


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

字串解包

>>> a,b,c = "abc"
>>> a
'a'
>>> b
'b'
>>> c
'c'

字典解包

>>> a,b,c = {"a":1, "b":2, "c":3}
>>> a
'a'
>>> b
'b'
>>> c
'c'

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

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

>>> a, b = 1, 2
>>> a
1
>>> b
2

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

>>> a = 1,
>>> a
(1,)

----------
>>> a = 1
>>> a
1

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

>>> a, b = 1, 2
>>> a, b = b, a
>>> a
2
>>> b
1

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

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

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

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

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

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

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

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

>>> 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 "", 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}

到此,關於 Python 解包給你介紹完了,如果本文對你有收穫,請點贊、轉發支持。

最後給你總結一下:

  1. 自動解包支持一切可迭代物件

  2. python3中,支持更高級的解包操作,用星號操作使得等號左邊的變數個數可以少於右邊迭代物件中元素的個數。

  3. 函式呼叫時,可以用 * 或者 ** 解包可迭代物件,作為引數傳遞

  4. python3.5,函式呼叫和運算式中可支持更多的解包操作。


●編號484,輸入編號直達本文

●輸入m獲取文章目錄

推薦↓↓↓

 

演算法與資料結構

更多推薦18個技術類微信公眾號

涵蓋:程式人生、演算法與資料結構、黑客技術與網絡安全、大資料技術、前端開發、Java、Python、Web開發、安卓開發、iOS開發、C/C++、.NET、Linux、資料庫、運維等。

赞(0)

分享創造快樂