iT邦幫忙

2022 iThome 鐵人賽

DAY 25
0

接續昨天 unpacking 的學習,今天我們來練習更進階的 unpacking。

假設我們現在分別要得到一個陣列的首位元素和剩餘元素,可能會這樣寫:

l = [1, 2, 3, 4, 5, 6]
a = l[0]
b = l[1:]
print(a)
print(b)
1
[2, 3, 4, 5, 6]

我們可以用 unpacking 技巧稍微簡化程式碼:

a, b = l[0], l[1:]
print(a)
print(b)
1
[2, 3, 4, 5, 6]

然而,Python3.6 以後引進了一個更酷炫的語法: * 運算元,它可以做到同樣的事:

a, *b = l
print(a)
print(b)
1
[2, 3, 4, 5, 6]

注意等號左邊的 * 運算元只能出現一次!

就像標準的 unpacking 一樣,這種延伸的 unpacking 方式也適用所有可迭代物件:

With tuples:

a, *b = -10, 5, 2, 100
print(a)
print(b)
-10
[5, 2, 100]

With strings:

a, *b = 'python'
print(a)
print(b)
p
['y', 't', 'h', 'o', 'n']

現在假設,我們需要取得首個、第二個、最後一個和其他所有元素

我們一樣可以用 slicing 做到:

s = 'python'

a, b, c, d = s[0], s[1], s[2:-1], s[-1]
print(a)
print(b)
print(c)
print(d)
p
y
tho
n

但使用 unpacking 技巧就讓一切變得更輕鬆了:

a, b, *c, d = s
print(a)
print(b)
print(c)
print(d)
p
y
['t', 'h', 'o']
n

如你所見, c 是 一群字母組成的陣列,並非字串。

如果你堅持要字串,就可以簡單地再加工:

print(c)
c = ''.join(c)
print(c)
['t', 'h', 'o']
tho

更酷的可能是,我們可以在等號右邊 unpacking,而且沒有等號左邊的『"*"只能出現一次』的限制:

l1 = [1, 2, 3]
l2 = [4, 5, 6]
l = [*l1, *l2]
print(l)
[1, 2, 3, 4, 5, 6]
l1 = [1, 2, 3]
s = 'ABC'
l = [*l1, *s]
print(l)
[1, 2, 3, 'A', 'B', 'C']

也可以對 set 或 dictionary 做這種 unpacking。

只是對 set 來說,這種 unpacking 可能不太好用,因為 set 是無序的,你恐怕不知道自己 unpacking 拿到什麼:

s = {10, -99, 3, 'd'}
for c in s:
    print(c)
10
3
-99
d

如你所見,unpacking 得到的元素順序和 set 創建時已經不同了。

s = {10, -99, 3, 'd'}
a, b, *c = s
print(a)
print(b)
print(c)
10
3
[-99, 'd']

那對 set unpacking 就都沒用嗎?

錯!看一下下面的 unpacking:

s = {10, -99, 3, 'd'}
*a, = s
print(a)
[10, 3, -99, 'd']

乍看之下沒什麼用,我們只是把 set 中的元素解開放入一個陣列,但這其實對 set 的運算很有幫助!

s1 = {1, 2, 3}
s2 = {3, 4, 5}

我們要怎麼把上面兩個 set 融合在一起?

s1 + s2
---------------------------------------------------------------------------

TypeError                                 Traceback (most recent call last)

/Users/maotingyang/Downloads/Extended Unpacking.ipynb Cell 35 in <cell line: 1>()
----> <a href='vscode-notebook-cell:/Users/maotingyang/Downloads/Extended%20Unpacking.ipynb#X46sZmlsZQ%3D%3D?line=0'>1</a> s1 + s2


TypeError: unsupported operand type(s) for +: 'set' and 'set'

看來加法沒用⋯⋯

我們可以用 set 內建的 union 方法:

print(s1)
print(s2)
s1.union(s2)
{1, 2, 3}
{3, 4, 5}





{1, 2, 3, 4, 5}

水!那如果我們要融合四個 set 呢?

s1 = {1, 2, 3}
s2 = {3, 4, 5}
s3 = {5, 6, 7}
s4 = {7, 8, 9}
print(s1.union(s2).union(s3).union(s4))
print(s1.union(s2, s3, s4))
{1, 2, 3, 4, 5, 6, 7, 8, 9}
{1, 2, 3, 4, 5, 6, 7, 8, 9}

寫起來比較累了,也許⋯⋯利用 unpacking?

{*s1, *s2, *s3, *s4}
{1, 2, 3, 4, 5, 6, 7, 8, 9}

我們直接把一個 set unpacking 到另一個 set,太神了!

這招對 dictionaries 一樣適用,只要記得 * 只是 unpacking dictionary 的 key:

d1 = {'key1': 1, 'key2': 2}
d2 = {'key2': 3, 'key3': 3}
[*d1, *d2]
['key1', 'key2', 'key2', 'key3']

有沒有辦法同時 unpack key-value 呢?

有的!我們可以用 ** operator:

d1 = {'key1': 1, 'key2': 2}
d2 = {'key2': 3, 'key3': 3}

{**d1, **d2}
{'key1': 1, 'key2': 3, 'key3': 3}

要注意,key2 的值變成了 3,因為 d2 在 d1 之後 unpacking,所以 d1 的值被 d2 覆寫:

{**d2, **d1}
{'key1': 1, 'key2': 2, 'key3': 3}

如此一來,字典也可以在字典中 unpacking,把字典融合了:

{'a': 1, 'b': 2, **d1, **d2, 'c':3}
{'a': 1, 'b': 2, 'key1': 1, 'key2': 3, 'key3': 3, 'c': 3}

Nested Unpacking

Python 甚至可以巢狀 unpacking:

a, b, (c, d) = [1, 2, ['X', 'Y']]
print(a)
print(b)
print(c)
print(d)
1
2
X
Y
a, b, (c, d) = [1, 2, 'XY']
print(a)
print(b)
print(c)
print(d)
1
2
X
Y

We can even write something like this:

a, b, (c, d, *e) = [1, 2, 'python']
print(a)
print(b)
print(c)
print(d)
print(e)
1
2
p
y
['t', 'h', 'o', 'n']

記得上面說到,等號左邊只能有一個 * unpacking⋯⋯

巢狀不在此限:

a, *b, (c, d, *e) = [1, 2, 3, 'python']
print(a)
print(b)
print(c)
print(d)
print(e)
1
[2, 3]
p
y
['t', 'h', 'o', 'n']

大部分解如下

a, *b, tmp = [1, 2, 3, 'python']
print(a)
print(b)
print(tmp)
1
[2, 3]
python
c, d, *e = tmp
print(c)
print(d)
print(e)
p
y
['t', 'h', 'o', 'n']

如果我們要用 slicing 達到跟上面同樣的效果:

l = [1, 2, 3, 'python']
l[0], l[1:-1], l[-1][0], l[-1][1], list(l[-1][2:])
(1, [2, 3], 'p', 'y', ['t', 'h', 'o', 'n'])
l = [1, 2, 3, 'python']
a, b, c, d, e = l[0], l[1:-1], l[-1][0], l[-1][1], list(l[-1][2:])
print(a)
print(b)
print(c)
print(d)
print(e)
1
[2, 3]
p
y
['t', 'h', 'o', 'n']

可以看到,相較這麼複雜的 slicing,用* unpacking 優雅許多。

好啦,我們明天見!

參考:Python 3: Deep Dive (Part 1 - Functional)


上一篇
Unpacking Iterables
下一篇
*args
系列文
小青蛇變大蟒蛇——進階Python學起來!30
圖片
  直播研討會
圖片
{{ item.channelVendor }} {{ item.webinarstarted }} |
{{ formatDate(item.duration) }}
直播中

尚未有邦友留言

立即登入留言