python列表(list)和元組(tuple)詳解

ckxllf發表於2021-03-15

  前言

  在我們實際開發中,經常需要將一組資料儲存起來,以便使用。如果學習了其他的語言可能知道陣列(Array)這個資料結構,它就可以將多個資料進行儲存,訪問資料可以透過陣列下標的方式,的進行獲取。如果你是python開發者,那麼可以使用更加靈活的列表(list)和元組(tuple),來進行資料儲存。下面我們先簡單瞭解下列表和元組的基本使用。

  列表

  列表是動態的,長度可以改變,可以隨意增加,修改或刪除元素。

  初始化列表

  a = list()

  b = []

  # 可以透過range快速建立list

  c = list(range(1,6))

  print("a:", a)

  print("b:", b)

  print("c:", c)

  # a: []

  # b: []

  # c: [1, 2, 3, 4, 5]

  新增元素

  append:在列表的末尾新增一個元素

  >>l = []

  >>l.append("python")

  >>l

  ['python']

  extend:使用可迭代物件中的所有元素來擴充套件列表

  >>l = ["python"]

  >>t = ["java"]

  >>l.extend(t)

  >>l

  ['python', 'java']

  insert:在給定的位置插入一個元素。第一個引數是要插入的元素的索引,所以 list_name.insert(0, x) 插入列表頭部

  >>l = ["python", "java"]

  >>l.insert(1,"go")

  >>l

  ['python', 'go', 'java']

  刪除元素

  remove(x):從列表中刪除值為x的第一項。 如果沒有需要刪除的值,那就丟擲異常

  >>l = ["python", "java"]

  >>l.remove("java")

  >>l

  ['python']

  >>l.remove("test")

  Traceback (most recent call last):

  File "", line 1, in

  ValueError: list.remove(x): x not in list

  pop: 刪除列表中給定位置的元素並返回它。如果沒有給定位置,pop() 將會刪除並返回列表中的最後一個元素

  >>l = ["python", "java", "go"]

  >>l.pop()

  'go'

  >>l

  ['python', 'java']

  >>l.pop(1)

  'java'

  >>l.pop(1)

  Traceback (most recent call last):

  File "", line 1, in

  IndexError: pop index out of range

  del: Python 中的關鍵字,專門用來執行刪除操作,它不僅可以刪除整個列表,還可以刪除列表中的某些元素

  >>l = ["python", "java", "go", "js"]

  >>del l[0:1]

  >>l

  ['java', 'go', 'js']

  >>del l[0]

  >>l

  ['go', 'js']

  clear(): 移除列表中的所有元素。等價於 del a[:]

  >>l = ["python", "java", "go", "js"]

  >>l.clear()

  >>l

  []

  ps: 這裡注意和del 的區別, clear是清空, del list_name 是刪除,記憶體也釋放

  修改元素

  修改單個可以透過下標的方法

  >>l = ["python", "go", "java"]

  >>l[0] = "PYTHON"

  >>l

  ['PYTHON', 'go', 'java']

  修改一組資料可以透過切片的方式

  >>l = ["python", "go", "java"]

  >>l[0:2] = "PYTHON", "GO"

  >>l

  ['PYTHON', 'GO', 'java']

  >>l[0:2] = ["python", "go"]

  >>l

  ['python', 'go', 'java']

  查詢元素

  index(x) :方法用來查詢某個元素在列表中出現的位置(也就是索引),如果該元素不存在,則會導致 ValueError 錯誤

  >>l

  ['python', 'go', 'java']

  >>l.index("python")

  0

  >>l.index("python1")

  Traceback (most recent call last):

  File "", line 1, in

  ValueError: 'python1' is not in list

  count() :用來統計某個元素在列表中出現的次數

  >>l

  ['python', 'go', 'java']

  >>l.count("PYTHON")

  0

  >>l.count("python")

  1

  其他操作

  sort:對列表中的元素進行排序

  >>l

  ['go', 'java', 'python']

  >>l.sort(reverse=True)

  >>l

  ['python', 'java', 'go']

  >>l.sort()

  >>l

  ['go', 'java', 'python']

  reverse: 反轉元素

  >>l = [1,2,3,4,5]

  >>l.reverse()

  >>l

  [5, 4, 3, 2, 1]

  copy: 返回列表的一個淺複製,等價於 a[:]

  >>l

  [5, 4, 3, 2, 1]

  >>a = l.copy()

  >>a

  [5, 4, 3, 2, 1]

  python列表使用場景

  1-使用列表實現棧

  棧(stack)特點就是後進先出, 使用列表實現是非常容易的,要新增一個元素到堆疊的頂端,使用 append() 。要從堆疊頂部取出一個元素,使用 pop() ,不用指定索引。

  stack = []

  stack.append(1)

  stack.append(2)

  stack.append(3)

  stack.append(4)

  stack.pop()

  # 4

  stack.pop()

  # 3

  stack.pop()

  # 2

  stack.pop()

  # 1

  # 注意捕捉錯誤

  2-實現佇列

  from collections import deque

  queue = deque(["python", "go", "java"])

  queue.append("python")

  queue.append("go")

  print(queue)

  queue.popleft()

  queue.popleft()

  print(queue)

  返回結果

  deque(['python', 'go', 'java', 'python', 'go'])

  deque(['java', 'python', 'go'])

  列表推導式

  a = [x ** 2 for x in range(10)]

  b = [(x, y) for x in [1, 2, 3] for y in [3, 1, 4] if x != y]

  # 巢狀列表推導式

  matrix = [

  [1, 2, 3, 4],

  [5, 6, 7, 8],

  [9, 10, 11, 12],

  ]

  c = [[row[i] for row in matrix] for i in range(4)]

  print("a:", a)

  print("b:", b)

  print("c:", c)

  返回

  a: [0, 1, 4, 9, 16, 25, 36, 49, 64, 81]

  b: [(1, 3), (1, 4), (2, 3), (2, 1), (2, 4), (3, 1), (3, 4)]

  c: [[1, 5, 9], [2, 6, 10], [3, 7, 11], [4, 8, 12]]

  元組

  元組是靜態,大小固定,不可以對元素進行增加,修改或刪除的操作

  建立元組

  a = 1, 2, 3

  print("a", a)

  b = (1, 2, 3)

  print("b", b)

  # 將字串轉換成元組

  tup1 = tuple("hello")

  print("將字串轉換成元組", tup1)

  # 將列表轉換成元組

  list1 = ['Python', 'Java', 'C++', 'JavaScript']

  tup2 = tuple(list1)

  print("將列表轉換成元組", tup2)

  # 將字典轉換成元組

  dict1 = {'a': 100, 'b': 42, 'c': 9}

  tup3 = tuple(dict1)

  print("將字典轉換成元組", tup3)

  # 將區間轉換成元組

  range1 = range(1, 6)

  tup4 = tuple(range1)

  print("將區間轉換成元組", tup4)

  返回結果

  a (1, 2, 3)

  b (1, 2, 3)

  將字串轉換成元組 ('h', 'e', 'l', 'l', 'o')

  將列表轉換成元組 ('Python', 'Java', 'C++', 'JavaScript')

  將字典轉換成元組 ('a', 'b', 'c')

  將區間轉換成元組 (1, 2, 3, 4, 5)

  訪問元素

  a = (1, 2, 3, 4, 5)

  # 透過下標

  print(a[0])

  # 透過切片:a[start : end : step]

  print(a[0:4:2])

  返回結果

  1

  (1, 3)

  刪除

  a = (1, 2, 3, 4, 5)

  del a

  元組和列表區別

  元組是靜態,列表是動態

  元組修改

  l = (1,2,3,4)

  id(l)

  # 4497372488

  l = l + (5,6)

  id(l)

  # 4494985832

  列表修改

  l = [1,2,3,4]

  id(l)

  # 4499169160

  l = l + [5,6]

  id(l)

  # 4495787016

  透過上面可以發現元組是不可以改變的,這裡強調一點很多新手對這個 l = l + (5,6) 很不難理解,不是說元組不可以修改的嗎,那為什麼這裡可以修改?記住這裡雖然可以執行,但是他是建立了一個新的元組,這時候的 l 不是原來的 l, 可以透過 id 查詢(或則執行 l[0] = -1 就會報錯)

  在這裡我多說幾句,這裡的靜態和動態,大白話來講是列表是可以進行列表的操作(新增,刪除,修改),一般操作行為下他的記憶體地址不變(透過id檢視),這和他的實現有關,但是元組就會改變,所以新的元組和原來的不一樣,一般時候有人(面試官或則開發不小心)會問你 a = ([1,2], 3,4), 為什麼可以進行a[0].append(3),但是id(a)前後不變,這就是0下標的元素是列表,列表可以修改的。

  列表需要更多記憶體,元組需要更少記憶體

  list_t = []

  print("列表初始化時候大小:", list_t.__sizeof__())

  tuple_t = ()

  print("元組初始化時候大小:", tuple_t.__sizeof__())

  返回結果

  列表初始化時候大小: 40

  元組初始化時候大小: 24

  看到結果有沒有發現列表比元組大18位元組,那麼問題來了:這18位元組是怎麼來的?這是由於列表是動態的,它需要儲存指標來指向對應的元素(佔用 8 個位元組)。另外,由於列表中元素可變,所以需要額外儲存已經分配的長度大小(佔用 8 個位元組),這樣才能實時追蹤列表空間的使用情況。但是對於元組,情況就不同了,元組長度大小固定,且儲存元素不可變,所以儲存空間也是固定的。

  列表不可被hash,元組可以被hash

  tuple_t = (1, 2)

  print("元組hash值:", hash(tuple_t))

  list_t = [1, 2]

  print("列表hash值:", hash(list_t))

  執行結果

  Traceback (most recent call last):

  File "/Users/linjian/MonitorCenter/MonitorCenter/apps/t6.py", line 4, in

  print("列表hash值:", hash(list_t))

  TypeError: unhashable type: 'list'

  元組hash值: 3713081631934410656

  從上面的結果可以發現元組是可以被hash,但列表卻是不可以。如果基礎紮實的應該會反應過來,python中hash需要滿足是不可變型別的資料結構(字串str、元組tuple、物件集objects)

  執行效率 大連做人流哪裡好

  # 初始化一個相同元素的列表和元組使用情況

  (djangoDemo) MonitorCenter % python -m timeit 'x=(1,2,3,4,5,6)'

  100000000 loops, best of 3: 0.0103 usec per loop

  (djangoDemo) MonitorCenter % python -m timeit 'x=[1,2,3,4,5,6]'

  10000000 loops, best of 3: 0.0514 usec per loop

  # 元組和列表索引操作對比

  (djangoDemo) MonitorCenter % python -m timeit 'x=(1,2,3,4,5,6)' 'y=x[3]'

  10000000 loops, best of 3: 0.0267 usec per loop

  (djangoDemo) MonitorCenter % python -m timeit 'x=(1,2,3,4,5,6)' 'y=x[3]'

  10000000 loops, best of 3: 0.0265 usec per loop

  上面的執行結果顯示: 元組初始化遠快於列表 ,大概有五倍的差距,但是索引操作的時候速度沒有多大差距

  截止目前為止,我們可以簡單總結列表和元組的區別有如下:

  元組使用tuple()或()初始化,列表使用list()或[]初始化

  元組是靜態,而列表是動態

  列表需要更多記憶體,元組需要更少記憶體

  列表不可被hash,元組可以被hash

  元組初始化效率高於列表,但索引操作沒有多大差距

  元組和列表使用場景

  再說使用場景前先講一下,在python後臺,對靜態資料做一些資源快取,通常因為垃圾回收機制的存在,一些變數不使用,python就會回收他們所佔的記憶體,但是對於一些靜態變數(比如說元組),當他們佔用不大時候(長度1~20的元組),python會暫時快取這部分記憶體,這樣下次就可以不再向作業系統發出請求,分配記憶體資源,而是直接使用用快取中之前的記憶體空間,這樣大大加快了程式的執行速度。所以一般有時候資料量不大,我經常使用元組替代列表。到目前為止我們可以簡單的總結出場景可以如下所示:

  如果資料不可變,我們就可以考慮使用元組,比如說性別型別,返回出去的城市資訊等等

  如果資料可變,我們就考慮使用列表,比如說使用者當天訪問的網頁等等

  擴充知識

  建立空的列表,是使用list()效率好還是[]?

  (djangoDemo) MonitorCenter % python -m timeit 'x=list()'

  10000000 loops, best of 3: 0.087 usec per loop

  (djangoDemo) MonitorCenter % python -m timeit 'x=[]'

  100000000 loops, best of 3: 0.0177 usec per loop

  透過上面的測試可以知道是[]快。list()函式呼叫,python中函式呼叫會建立stack並且會進行引數檢查,[]是一個內建C函式,可以直接呼叫,因此效率更高。

  執行相乘操作時候,是 *= 效率好, 還是*?

  (djangoDemo) MonitorCenter % python -m timeit 'x = [1,2,3]' 'x*=3'

  10000000 loops, best of 3: 0.0903 usec per loop

  (djangoDemo) MonitorCenter % python -m timeit 'x = [1,2,3]' 'x = x * 3'

  10000000 loops, best of 3: 0.104 usec per loop

  從結果可以看出是*效率會低點。*= 中會預分配,不足的時候擴容,但是* 會按照每次的量進行分配大小

  為什麼輸出是這樣的?

  list_1 = [1, 2, 3, 4]

  list_2 = [1, 2, 3, 4]

  list_3 = [1, 2, 3, 4]

  list_4 = [1, 2, 3, 4]

  for idx, item in enumerate(list_1):

  del item

  for idx, item in enumerate(list_2):

  list_2.remove(item)

  for idx, item in enumerate(list_3[:]):

  list_3.remove(item)

  for idx, item in enumerate(list_4):

  list_4.pop(idx)

  print("list_1", list_1)

  print("list_2", list_2)

  print("list_3", list_3)

  print("list_4", list_4)

  結果

  list_1 [1, 2, 3, 4]

  list_2 [2, 4]

  list_3 []

  list_4 [2, 4]

  list_2為什麼輸出是[2,4]? 因為在第一次刪除後,list_2變成了 [2,3,4], 然後在刪除輪循到到第二個資料也就是3(大部分都以為是2,但是2從原來的下表2變為1),可以參看下面的

  give next element: 0

  0 ---> 1

  1 2

  2 3

  3 4

  give next element: 1

  0 2

  1 ---> 3

  2 4

  give next element: 2

  0 2

  1 4

  list_3 為什麼是[], 還記得之前我們說copy時候,copy等於[:](淺複製),所以輪詢的和刪除的不是同一記憶體的資料。

  list_4可以結合list_2思考,因為第一次刪除,第二次刪除是下標2,但是資料變了,下標2的資料不是原來的2,而是3.

來自 “ ITPUB部落格 ” ,連結:http://blog.itpub.net/69945560/viewspace-2762971/,如需轉載,請註明出處,否則將追究法律責任。

相關文章