天天看點

Python應用——多變量的靈活處理

本文始發于個人公衆号:TechFlow,原創不易,求個關注

我們都知道Python是一個非常靈活的語言,以至于如果它不是你的第一門語言,你會發現它總能給你各種各樣的驚喜,讓你忍不住驚歎:woc,還有這種操作。尤其我在系統地學習Python之前是Java後端出身,是以每一階段幾乎都會讓我覺得打開了新世界的大門。今天就和大家介紹一個最基礎,非常好用,但是很多人不知道的操作。

解壓變量

我們都知道,Python允許進行多個變量的指派操作,比如著名的交換兩個元素,如果是在C++或者Java語言當中,如果不通過函數實作,必須要引入第三個變量,比如:

# swap a, b
c = a
a = b
b = c
           

我們要交換a和b必須要引入c,這是因為當我們指派b給a的時候,a原本的值會丢失,是以我們必須要先”緩存“下來。但是由于Python支援多變量指派的操作,是以大可不必引入其他變量就可以完成,是以交換兩個元素在Python當中隻有一行就可以搞定:

a, b = b, a
           

Python的解釋器會直接計算後邊的值然後覆寫左邊,指派是同時進行的,是以不需要引入其他變量,而且看起來也非常geek。

除此之外,Python還支援tuple和list的解壓。

舉個例子,假設我們有一個二進制數組:[1, 2],我們希望用兩個變量分别擷取它的第0位和第一位,我們當然可以寫成這樣:

l = [1, 2]
a, b = l[0], l[1]
           

其實并不用這麼麻煩,因為當Python檢測到等号左邊是多個變量,右邊是list或者是tuple之後,會自動執行list和tuple的解壓,将它依次指派給對應的元素,是以上面的代碼可以簡化成:

l = [1, 2]
a, b = l
           

那如果l是一個二維數組,我們希望周遊它呢?同樣可以在循環當中使用:

l = [[1, 2], [3, 4], [5, 6]]
for i, j in l:
    print(i, j)
           

即使是在變量的組合當中也可以生效:

a, b, c = 1, 3, (4, 5)
print(c)
           

當我們執行這段代碼,螢幕上會輸出什麼呢?是會報錯嗎?還是會解壓(4, 5)這個tuple然後将4指派給c呢?

都不對,輸出的結果是(4, 5),也就是說Python發現變量數量對不上之後,會自動将tuple當做一個整體進行指派。不但如此,即使是下面這種情況,Python也能自動識别:

a, b, (c, d), e = 1, 3, (4, 5), 7
print(c, d)
           

在上面的指派當中,既有tuple又有普通元素,并且我們的變量也組合成了tuple,這時Python同樣會識别出(4, 5)應該指派給(c, d)這個整體,也就是說4和5分别指派給c和d。

預設元素

在有的時候,我們在擷取元素的時候,源資料當中有我們不需要的字段。雖然Python自動解壓非常友善,但是我們還是要為我們不需要的資料設定變量。在一些情況下這會導緻記憶體的浪費,并且這也不符合我們程式設計的規範,即所有變量都應該派上用場。為了解決這個問題,Python提供預設元素的方法。我們可以使用_來代表一個預設值,_對應的資料不會被存儲下來,隻是為了友善我們”湊齊“元素。

舉個例子,還用上面的例子舉例,假設源資料的格式是這樣:1, 3, (4, 5), 7,但是我們隻需要中間的元組,我們就可以這樣去接收:

_, _, (c, d), _ = 1, 3, (4, 5), 7
           

再比如,當我們周遊dict的時候,有可能我們并不關注dict的key,隻希望獲得它的value,這個時候也可以使用預設符号:

a = {}
for _, v in a.items():
    print(v)
           

壓縮變量

既然變量可以解壓,那麼自然也可以壓縮。想象一個場景,比如有一批衡量工廠零件的資料,這個資料當中除了零件的尺寸之外還包含了零件的名稱,生産日期和工廠名稱等等其他的屬性。假設我們當下希望解析這份資料,并且将零件的尺寸用數組存儲,這個時候應該怎麼辦呢?

比如,零件的資料的規格長這樣:wheel, factory1, 3, 4, 5, 6, 2020-02-02

Python同樣針對這個問題提供了解決方法,就是變量壓縮符*,針對上面那個問題,我們可以寫成:

data = ['wheel', 'factory1', 3, 4, 5, 6, '2020-02-02']
name, factory, *inch, date = data
print(inch)
           

最後我們列印出來的inch是[3, 4, 5, 6],也就是說通過使用*,我們成功地将中間表示零件尺寸的資料指派進了一個數組當中。這個操作非常重要,因為有可能不同零件尺寸的數量是不同的,如果我們自己寫解析的話就很難處理這個問題。而使用Python當中的 *操作符,我們可以很好地解決這個問題。

聯合使用

到這裡,我們介紹了預設符号的用法,介紹了壓縮符号的用法,問題來了,我們能不能将這兩個符号組合使用,擷取資料當中任意個預設值呢?

當然是可以的,還是剛才的問題,假設我們現在不關心零件的尺寸,想要過濾掉它們,我們隻要對上面的代碼稍作改動即可:

data = ['wheel', 'factory1', 3, 4, 5, 6, '2020-02-02']
name, factory, *_, date = data
           

如此我們就過濾掉了中間若幹個尺寸資訊,僅僅保留了頭尾其他的資訊。

其他用途

到這裡還沒結束,不知道大家在看到 * 這個操作符号的時候有沒有什麼聯想,如果稍稍了解過Python的話,應該會想起Python當中,如果我們想讓一個函數接收任何參數的話,我們可以寫成:

def func(*args, **kw):
    pass
           

其中args其實代表一個數組,kw代表一個dict,這些我們都是知道的。但是前面的 * 和 ** 呢,又代表什麼呢?

代表解壓數組,*自然就代表解壓dict。我們來看個例子:

a = [1, 3, 5]
print(a)
print(*a)
           

請問print(a)和print(a)有什麼差別?如果你試一下就會發現,直接列印a,出來的結果是[1, 3, 5],如果你列印 a,得到的結果是1, 3, 5。也就是說前者是将a當成一個數組輸出,是一個變量,後者則是将a解壓了,當成了3個變量輸出。那麼同樣的道理,*kw,也是将作為dict的kw解壓,以key: value的形式展開。不過如果你直接調用 *kw會得到一個報錯,這個操作隻能在函數傳遞參數的時候使用。

是以到這裡,我們就明白了,*args和*kw為什麼能夠代表所有參數了。因為前者代表了直接傳遞的必選參數,後者呢,代表提供了預設值的預設參數。這也是為什麼Python限定了預設參數必須放在必選參數後面的原因,一方面是為了消除歧義,另一方面也是為了能夠用*args, *kw來統一表示。

今天的内容雖然簡單,但是在實際代碼當中經常用到,用得好的話可以大大簡化我們coding的難度以及代碼的美觀程度,是以如果對Python感興趣的同學,非常推薦一學。

今天的文章就是這些,如果覺得有所收獲,請順手掃碼點個關注吧,你們的舉手之勞對我來說很重要。

參考資料

Python cookbook 第三版

維基百科