1. 程式人生 > 其它 >Python內建資料結構大總結

Python內建資料結構大總結

內建據結構大總結 今天不講解新的內容,主要回顧一下以往講過的內建資料結構,來個大總結。

五種線性結構

  1. 列表
  2. 元組
  3. 字串
  4. bytes
  5. bytearray

兩種非線性結構

  1. 字典
  2. 集合

列表、元組、字串屬於線性結構,我們可以對其進行切片操作、解包/封包操作。

序列型別操作符

下表是所有序列型別都適用的操作符:

序列操作符

作用

seq[ind]

獲得下標為ind的元素

seq[ind1:ind2]

獲得下標從ind1到ind2間的元素集合

seq * expr

序列重複expr次

seq1 + seq2

連線序列seq1和seq2

obj in seq

判斷obj元素是否包含在seq中

obj not in

判斷obj元素是否不包含在seq中

幾種資料結構的共性

這幾種資料結構的共性:

  1. 都是順序儲存
  2. 順序訪問
  3. 可迭代物件(可迭代物件可以用len方法獲取其長度)
  4. 通過索引進行元素的訪問
  5. 可以進行切片操作

切片

切片不會對原有的序列做任何修改,切片的語法為:

seq[start:stop]

從索引start開始,到索引stop結束,不包含stop,返回新的序列,不會對原有的物件做任何修改。

幾個特性:

  • start超出索引範圍:start = 0
  • stop超出索引範圍:stop = -1
  • 負數索引:實際上可轉化為:len(seq) + index
  • start >= stop
    時,返回空列表

slice的實現:

lst = list(range(0, 10))


def slice(lst, start=0, stop=0):
    if start < 0:
        start = len(lst) + start
    if stop <= 0:
        stop = len(lst) + stop
    if stop <= start:
        return []
    if stop > len(lst):
        stop = len(lst)
    if start < 0:
        start = 0
    ret = []
    for i, v in enumerate(lst):
        if i >= start and i < stop:
            ret.append(v)
    return ret

print(slice(lst, 3, 2))
print(slice(lst, 2, 5))
print(slice(lst, -100, 100))

執行結果為:

 : []
 : [2, 3, 4]
 : [0, 1, 2, 3, 4, 5, 6, 7, 8, 9]

如果有了步長之後,上面的規則就會發生變化。接下來加入步長的slice實現:

def slice(lst, start=0, stop=0, step=1):
    ret = []
    if stop < 0:
        tmp = start
        start = tmp
        stop = start
    current = start
    while current < stop:
        try:
            ret.append(lst[current])
        except IndexError:
            pass
        current += step
    return ret

切片的一些常用操作:

>>> lst = list(range(0, 10))
>>> lst
[0, 1, 2, 3, 4, 5, 6, 7, 8, 9]

>>> lst[:] # 等效於copy方法

>>> lst[-5:-3] # 支援負數索引

# start大於等於stop時,返回空列表
>>> lst[3:1]

# 列出偶數,步長為2
lst[::2]
[0, 2, 4, 6, 8]

# 列出偶數,步長為2,並倒序輸出
lst[::2][::-1]
[8, 6, 4, 2, 0]

# 列出奇數,步長為2,並倒序輸出
lst[::-2]
[9, 7, 5, 3, 1]

# 列出偶數,步長為2,並倒序輸出
lst[-2::-2]
[8, 6, 4, 2, 0]

索引

如果索引超出範圍,將引發IndexError的異常。修改元素的時候,如果超出索引範圍,也同樣引發IndexError異常。

  • index(value)方法根據value找索引
  • count(value)方法統計value出現的次數

enumerate的實現:

def enumerate(iterator):
    i = 0
    for v in iterator:
        yield i, v
        i += 1

def enumerate(iterator):
    ret = []
    i = 0
    for v in iterator:
        ret.append((i, v))
        i += 1
    return ret

引用

列表批量賦值:

## 當賦值的序列連續時
# 對切片賦值,會替代原來的元素
>>> lst = list(range(0, 10))
>>> lst
[0, 1, 2, 3, 4, 5, 6, 7, 8, 9]
>>> lst[3:5] = ['x', 'y', 'z']
>>> lst
[0, 1, 2, 'x', 'y', 'z', 5, 6, 7, 8, 9]
>>> lst = list(range(0, 10))
>>> lst[3:5] = ['x']
>>> lst
[0, 1, 2, 'x', 5, 6, 7, 8, 9]
>>> lst = list(range(0, 10))
>>> lst[3:5] = 'x'
>>> lst
[0, 1, 2, 'x', 5, 6, 7, 8, 9]
## 當賦值的序列不連續時
>>> lst = list(range(0, 10))
>>> lst[3:8:2] = ['x', 'y', 'z']
>>> lst
[0, 1, 2, 'x', 4, 'y', 6, 'z', 8, 9]
>>> lst = list(range(0, 10))
>>> lst[3:8:2] = ['x']
ValueError: attempt to assign sequence of size 1 to extended slice of size 3
>>> lst
[0, 1, 2, 3, 4, 5, 6, 7, 8, 9]

不建議使用以上的方式對切片賦值的操作

解包/封包

解構與封裝可以叫做解包與封包。

  • 解構把集合裡的元素複製給變數;
  • 封裝是用變數構建元組。

解構:按照元素順序,把線性解構的元素依次賦值給變數。

封裝的例子:

t = 1, 2
print(t)
(1, 2)

print(type(t))
<class 'tuple'>

定義一個元組,可以省略小括號。

t1 = (1, 2)
t2 = 1, 2
print(t1 == t2) # t1與t2是等效的
True

封裝出來的是元組。封裝沒有什麼難度。解構的變化多樣,接下來重點看看解構。

先看一個例子:

In[29]: def swap(a, b):
    ...:     i = a
    ...:     a = b
    ...:     b = i
    ...:     return (a, b)
    ...: 

In[30]: swap(1, 3)
Out[30]: (3, 1)

對上面的程式碼進行改寫,由3行程式碼,變成了一行程式碼:

In[31]: def swap(a, b):
    ...:     a, b = b, a
    ...:     return (a, b)
    ...: 

In[32]: swap(1, 3)
Out[32]: (3, 1)

對於如下的程式碼操作,就是解包:

In[33]: x, y = (1, 3)

In[34]: x
Out[34]: 1

In[35]: y
Out[35]: 3

上面的程式碼使用的是元組,列表也是可以的:

In[36]: a, b = 1, 3

In[37]: a
Out[37]: 1

In[38]: b
Out[38]: 3

接下來看一下封包:

In[39]: t = 1, 3

In[40]: t
Out[40]: (1, 3)

In[41]: type(t)
Out[41]: tuple

繼續看例子:

In[42]: head, tail = list(range(0, 10))
# 將會得到如下的錯誤,因為=兩邊的元素數量不一致導致的
ValueError: too many values to unpack (expected 2)

In[43]: head, *tail = list(range(0, 10))

In[44]: head
Out[44]: 0

In[45]: tail
Out[45]: [1, 2, 3, 4, 5, 6, 7, 8, 9]

In[46]: *head, tail = list(range(0, 10))

In[47]: head
Out[47]: [0, 1, 2, 3, 4, 5, 6, 7, 8]

In[48]: tail
Out[48]: 9

如果對一個含有2個元素的列表進行解包:

In[49]: head, *tail = [1, 2]

In[50]: head
Out[50]: 1

In[51]: tail
Out[51]: [2]

如果對一個含有一個元素的列表進行解包:

In[52]: head, *tail = [1]

In[53]: head
Out[53]: 1

In[54]: tail
Out[54]: []

如果對一個空列表進行解包:

In[55]: head, *tail = []
ValueError: not enough values to unpack (expected at least 1, got 0)

針對上述例子的總結:

  1. 左邊不能只有一個星號,還要有其他元素
  2. 如果左邊不用星號,那麼左邊的元素個數要與右邊的元素個數相同
  3. 左邊變數數小於右邊元素個數,且左邊沒有加星號會報錯
  4. 元素按照順序賦值給變數
  5. 變數和元素必須匹配
  6. 加星號變數,可以接收任意個數的元素
  7. 加星號的變數不能單獨出現

針對上述,寫一個具體的例子:

def it(lst):
    if lst:
        head, *tail = lst
        print(head)
        it(tail)

it(list(range(0, 10)))
0
1
2
3
4
5
6
7
8
9

更復雜一點的例子:

In[63]: head, *tail = [1, 2, 3]

In[64]: head
Out[64]: 1

In[65]: tail
Out[65]: [2, 3]

下面這個例子,在Python2中不能實現:

In[59]: head, *mid, tail = [1, 2, 3, 4, 5]

In[60]: head
Out[60]: 1

In[61]: mid
Out[61]: [2, 3, 4]

In[62]: tail
Out[62]: 5

接下來還有更好遠的,如果我們要丟棄=右邊某個值,可以使用下劃線來,演示如下:

In[66]: lst = list(range(0, 10))

In[67]: lst
Out[67]: [0, 1, 2, 3, 4, 5, 6, 7, 8, 9]

In[68]: a, b, _, c, *_ = lst

In[69]: a
Out[69]: 0

In[70]: b
Out[70]: 1

In[71]: c
Out[71]: 3

如果我們只想要序列的首位兩個元素,可以這樣操作:

In[72]: head, *_, tail = lst

In[73]: head
Out[73]: 0

In[74]: tail
Out[74]: 9

再來一發,兩邊結構要一樣:

In[75]: lst = [1, [2, 3], 4]

In[76]: a, (b, c), d = lst

In[77]: a
Out[77]: 1

In[78]: b
Out[78]: 2

In[79]: c
Out[79]: 3

In[80]: d
Out[80]: 4

對上面的例子,再來稍微變化一下,不過兩邊的結構要一樣,解構是支援多層次的。:

In[81]: lst = [1, [2, 3, 4, 5, 6, 8], 9]

In[82]: lst
Out[82]: [1, [2, 3, 4, 5, 6, 8], 9]

In[83]: a, (b, *_, c), d = lst

In[84]: a
Out[84]: 1

In[85]: b
Out[85]: 2

In[86]: c
Out[86]: 8

In[87]: d
Out[87]: 9

注意:

  • 解包的時候,兩邊的結構要一致 (重要的事情說三遍)
  • 解包的時候,兩邊的結構要一致 (重要的事情說三遍)
  • 解包的時候,兩邊的結構要一致 (重要的事情說三遍)
  • 只要兩邊結構一樣就行
>>> a, (b, (c, (d,))) = [1, [2, [3, [4]]]]
>>> a
1
>>> b
2
>>> c
3
>>> d
4

python的一個慣例,使用單個下劃線表示丟棄該變數。單個下劃線也是Python合法的識別符號,但是如果不是要丟棄一個變數,通常不要用單個下劃線表示一個有意義的變數。

head, *_ = 'I love python'
print(head)
I
key, *_, value = 'env = properties'.partition('=')
print(key)
env
print(value)
properties

非常複雜的資料結構,多層巢狀的線性結構的時候,可以用解構快速提取其中的值。

本文總結

這是一個沒有小結的小結,還是希望大家都能學會Python,能夠上手寫一定的程式碼並用到工作當中。最後,祝大家學習愉快,在學習的路上你並不孤單,加油。