能取值亦能賦值的Python切片

dongfanger發表於2021-02-23

切片,就像麵包,給幾刀,切成一片一片,可以做成吐司,也可以做成三明治,口味更佳:

列表(list)、元組(tuple)、字串(str)都能進行切片,得到子片段,實際上切片操作比想象的要強大很多,能取值,亦能賦值。

忽略最後一個元素

切片是用下標和冒號來描述的,比如s[2:13]。對於2, 3, ..., 12這個序列,表達為[2, 13),左閉右開,比[2, 12](1, 13)都更合理,理由如下:

  1. 上限減去下限等於元素個數,比如13 - 2 = 11,剛好就有11個元素。
  2. 連續的範圍沒有重疊,比如[2, 13)[13, 25) 是兩個連續的範圍,13只會包含在後一個裡。

下標從0開始

對於10個元素,寫成[0, 10)[1, 11)更合理,理由如下:

  1. N個元素,[0, N)[1, N+1)寫法更簡潔,不需要+1

  2. 某個元素的下標等於排在它前面元素的個數,方便使用,比如:

    0 1 2 3 4 5 6 7 8 9
            ^ 
       前面有4個元素
    

好用的切片

以上兩個數學理論給切片使用帶來了很多好處:

  • 當只有最後一個位置資訊時,可以快速看出有幾個元素,比如my_list[:3]返回3個元素。

  • 當起止位置資訊都可見時,可以快速計算出長度,用stop - start就可以了,比如my_list[1:3]長度為2。

  • 利用任意一個下標把序列切割成不重疊的兩部分,只要寫成my_list[:x]my_list[x:]就可以了,比如

    >>> my_list = [10, 20, 30, 40, 50, 60]
    >>> my_list[:3]
    [10, 20, 30]
    >>> my_list[3:]
    [40, 50, 60]
    

Python裡的範圍(range)也是忽略最後一個元素,下標從0開始的。

切片間隔

切片除了s[a:b],還有第三個下標s[a:b:c],意思是對s在a和b之間以c為間隔取值,c還可以為負,負值意味著反向取值。比如:

>>> s = "bicycle"
>>> s[::3]
"bye"
>>> s[::-1]
"elcycib"
>>> s[::-2]
"eccb"

a:b:c更嚴謹的描述是start:stop:step

語法如此簡潔,用腳想也知道是Python魔法方法乾的好事!在對s[a:b:c]進行求值的時候,Python實際上會呼叫s.__getitem__(slice(a, b, c)),熟悉的配方,熟悉的味道。slice(a, b, c)a:b:c用在[]中返回的切片物件,slice()是Python內建函式,示例:

invoice = "Mini Kit $34.95 1 $ 34.95"
SKU = slice(0, 8)
print(invoice[SKU])

切片賦值

切片有一個強大功能是給切片賦值,如果把切片放在賦值語句的左邊,或把它作為del操作的物件,我們就可以對序列進行嫁接、切除或就地修改操作。示例:

>>> l = list(range(10))
>>> l
[0, 1, 2, 3, 4, 5, 6, 7, 8, 9]
>>> del l[5:7]
>>> l
[0, 1, 2, 3, 4, 7, 8, 9]
>>> l[3:2] = [11, 22]
>>> l
[0, 1, 2, 11, 22, 3, 4, 7, 8, 9]
>>> l[2:5] = [100]
>>> l
[0, 1, 100, 3, 4, 7, 8, 9]

注意,如果賦值的物件是一個切片,那麼賦值語句的右側必須是個可迭代物件,即使只有單獨一個值,否則會報錯:

>>> l[2:5] = 100
Traceback (most recent call last):
  File "<input>", line 1, in <module>
TypeError: can only assign an iterable

多維切片

除了一維切片,Python還支援多維切片,這在多維陣列中能體現出來。NumPy是Python第三方庫,提供了高階陣列,使得Python成為科學計算應用的主流語言。示例:

>>> import numpy
>>> a = numpy.arange(12)
>>> a
array([ 0,  1,  2,  3,  4,  5,  6,  7,  8,  9, 10, 11])
>>> a.shape
(12,)
>>> a.shape = 3, 4
>>> a
array([[ 0,  1,  2,  3],
       [ 4,  5,  6,  7],
       [ 8,  9, 10, 11]])
>>> a[:, 1]
array([1, 5, 9])
>>> a[1:2, 2:3]
array([[6]])
>>> a[1:3, 2:4]
array([[ 6,  7],
       [10, 11]])

在NumPy中,省略號...用作多維陣列切片的快捷方式,如果x是四維陣列,那麼x[i, ...]就是x[i, :, :, :]的縮寫,比如:

>>> a.shape = 2, 2, 3
>>> a
array([[[ 0,  1,  2],
        [ 3,  4,  5]],
       [[ 6,  7,  8],
        [ 9, 10, 11]]])
>>> a[:, :, 1]
array([[ 1,  4],
       [ 7, 10]])
>>> a[..., 1]
array([[ 1,  4],
       [ 7, 10]])

小結

本文介紹了Python強大的切片操作,因為忽略最後一個元素和下標從0開始,所以切片用起來特別順手,除了開始和結尾,還能設定切片間隔,間隔為負可以反向取值。切片賦值是切片另一個強大功能,需要注意的是賦值語句的右側必須是個可迭代物件。

參考資料:

《流暢的Python》

https://blog.wz52.cn/archives/174.html

相關文章