從零開始學Python:第十二課-常用資料結構之元組

千鋒Python唐小強發表於2020-07-03

上一節課為大家講解了Python中的列表,它是一種容器型資料型別,我們可以透過定義列表型別的變數來儲存和操作多個元素。當然,Python中容器型的資料型別肯定不止列表一種,接下來我們為大家講解另一種重要的容器型資料型別,它的名字叫元組(tuple)。

從零開始學Python:第十二課-常用資料結構之元組

定義和使用元組

在Python中,元組也是多個元素按照一定的順序構成的序列。元組和列表的不同之處在於,元組是不可變型別,這就意味著元組型別的變數一旦定義,其中的元素不能再新增或刪除,而且元素的值也不能進行修改。定義元組通常使用()字面量語法,元組型別支援的運算子跟列表是一樣。下面的程式碼演示了元組的定義和運算。

# 定義一個三元組

t1 = ( 30, 10, 55)
# 定義一個四元組
t2 = ( '駱昊', 40, True, '四川成都')

# 檢視變數的型別
print( type(t1), type(t2))    # <class 'tuple'> <class 'tuple'>
# 檢視元組中元素的數量
print( len(t1), len(t2))      # 3 4

# 透過索引運算獲取元組中的元素
print(t1[ 0], t1[ -3])         # 30 30
print(t2[ 3], t2[ -1])         # 四川成都 四川成都

# 迴圈遍歷元組中的元素
for member in t2:
    print(member)

# 成員運算
print( 100 in t1)    # False
print( 40 in t2)     # True

# 拼接
t3 = t1 + t2
print(t3)           # ( 30, 10, 55, '駱昊', 40, True, '四川成都')

# 切片
print(t3[:: 3])      # ( 30, '駱昊', '四川成都')

# 比較運算
print(t1 == t3)    # False
print(t1 >= t3)    # False
print(t1 < ( 30, 11, 55))    # True

一個元組中如果有兩個元素,我們就稱之為二元組;一個元組中如果五個元素,我們就稱之為五元組。需要提醒大家注意的是,()表示空元組,但是如果元組中只有一個元素,需要加上一個逗號,否則()就不是代表元組的字面量語法,而是改變運算優先順序的圓括號,所以('hello', )和(100, )才是一元組,而('hello')和(100)只是字串和整數。我們可以透過下面的程式碼來加以驗證。

# 空元組

a = ()
print( type(a))    # <class 'tuple'>
# 不是元組
b = ( 'hello')
print( type(b))    # <class 'str'>
c = ( 100)
print( type(c))    # <class 'int'>
# 一元組
d = ( 'hello', )
print( type(d))    # <class 'tuple'>
e = ( 100, )
print( type(e))    # <class 'tuple'>

元組的應用場景

講到這裡,相信大家一定迫切的想知道元組有哪些應用場景,我們給大家舉幾個例子。

例子1:打包和解包操作。

當我們把多個用逗號分隔的值賦給一個變數時,多個值會打包成一個元組型別;當我們把一個元組賦值給多個變數時,元組會解包成多個值然後分別賦給對應的變數,如下面的程式碼所示。


# 打包

a = 1, 10, 100
print(type(a), a)    # <class 'tuple'> (1, 10, 100)
# 解包
i, j, k = a
print(i, j, k)       # 1 10 100

在解包時,如果解包出來的元素個數和變數個數不對應,會引發ValueError異常,錯誤資訊為:too many values to unpack(解包的值太多)或not enough values to unpack(解包的值不足)。


a 
= 
1
, 
10
, 
100
, 
1000

# i, j, k = a             # ValueError: too many values to unpack (expected 3)
# i, j, k, l, m, n = a    # ValueError: not enough values to unpack (expected 6, got 4)

有一種解決變數個數少於元素的個數方法,就是使用星號表示式,我們之前講函式的可變引數時使用過星號表示式。有了星號表示式,我們就可以讓一個變數接收多個值,程式碼如下所示。需要注意的是,用星號表示式修飾的變數會變成一個列表,列表中有0個或多個元素。還有在解包語法中,星號表示式只能出現一次。


a 
= 
1
, 
10
, 
100
, 
1000

i, j, *k = a
print(i, j, k)           # 1 10 [100, 1000]
i, *j, k = a
print(i, j, k)          # 1 [10, 100] 1000
*i, j, k = a
print(i, j, k)          # [1, 10] 100 1000
*i, j = a
print(i, j)             # [1, 10, 100] 1000
i, *j = a
print(i, j)             # 1 [10, 100, 1000]
i, j, k, *l = a
print(i, j, k, l)       # 1 10 100 [1000]
i, j, k, l, *m = a
print(i, j, k, l, m)    # 1 10 100 1000 []

需要說明一點,解包語法對所有的序列都成立,這就意味著對字串、列表以及我們之前講到的range函式返回的範圍序列都可以使用解包語法。大家可以嘗試執行下面的程式碼,看看會出現怎樣的結果。

a, b, *
c = range(
1, 
10)

print(a, b, c)
a, b, c = [ 1, 10, 100]
print(a, b, c)
a, *b, c = 'hello'
print(a, b, c)

現在我們可以反過來思考一下函式的可變引數,可變引數其實就是將多個引數打包成了一個元組,可以透過下面的程式碼來證明這一點。

def add(*args):

   print(type(args), args)
   total = 0
    for val in args:
       total += val
    return total


add( 1, 10, 20)        # < class ' tuple'> ( 1, 10, 20)
add( 1, 2, 3, 4, 5)    # < class ' tuple'> ( 1, 2, 3, 4, 5)

例子2:交換兩個變數的值。

交換兩個變數的值是程式語言中的一個經典案例,在很多程式語言中,交換兩個變數的值都需要藉助一箇中間變數才能做到,如果不用中間變數就需要使用比較晦澀的位運算來實現。在Python中,交換兩個變數a和b的值只需要使用如下所示的程式碼。

a, b = b, a

同理,如果要將三個變數a、b、c的值互換,即b賦給a,c賦給b,a賦給c,也可以如法炮製。

a, b, c = b, c, a

需要說明的是,上面並沒有用到打包和解包語法,Python的位元組碼指令中有ROT_TWO和ROT_THREE這樣的指令可以實現這個操作,效率是非常高的。但是如果有多於三個變數的值要依次互換,這個時候沒有直接可用的位元組碼指令,執行的原理就是我們上面講解的打包和解包操作。

例子3:讓函式返回多個值。

有的時候一個函式執行完成後可能需要返回多個值,這個時候元組型別應該是比較方便的選擇。例如,編寫一個找出列表中最大值和最小的函式。



def 
find_max_and_min
(items):

    """找出列表中最大和最小的元素
   :param items: 列表
   :return: 最大和最小元素構成的二元組
   """
   max_one, min_one = items[ 0], items[ 0]
    for item in items:
        if item > max_one:
           max_one = item
        elif item < min_one:
           min_one = item
    return max_one, min_one

上面函式的return語句中有兩個值,這兩個值會組裝成一個二元組然後返回。所以呼叫find_max_and_min函式會得到這個二元組,如果願意也可以透過解包語法將二元組中的兩個值分別賦給兩個變數。

元組和列表的比較

這裡還有一個非常值得探討的問題,Python中已經有了列表型別,為什麼還需要元組這樣的型別呢?這個問題對於初學者來說似乎有點困難,不過沒有關係,我們先丟擲觀點,大家可以一邊學習一邊慢慢體會。

  • 元組是不可變型別, 不可變型別更適合多執行緒環境,因為它降低了併發訪問變數的同步化開銷。關於這一點,我們會在後面講解多執行緒的時候為大家詳細論述。
  • 元組是不可變型別,通常 不可變型別在建立時間和佔用空間上面都優於對應的可變型別。我們可以使用sys模組的getsizeof函式來檢查儲存相同元素的元組和列表各自佔用了多少記憶體空間。我們也可以使用timeit模組的timeit函式來看看建立儲存相同元素的元組和列表各自花費的時間,程式碼如下所示。

import sys

import timeit

a = list( range( 100000))
b = tuple( range( 100000))
print(sys.getsizeof(a), sys.getsizeof(b))    # 900120 800056

print(timeit.timeit( '[1, 2, 3, 4, 5, 6, 7, 8, 9]'))
print(timeit.timeit( '(1, 2, 3, 4, 5, 6, 7, 8, 9)'))
  • Python中的元組和列表是可以相互轉換的,我們可以透過下面的程式碼來做到。
# 將元組轉換成列表

info = ( '駱昊', 175, True, '四川成都')
print(list(info))       # [ '駱昊', 175, True, '四川成都']
# 將列表轉換成元組
fruits = [ 'apple', 'banana', 'orange']
print(tuple(fruits))    # ( 'apple', 'banana', 'orange')

簡單的總結

列表和元組都是容器型的資料型別,即一個變數可以儲存多個資料。 列表是可變資料型別元組是不可變資料型別,所以列表新增元素、刪除元素、清空、排序等方法對於元組來說是不成立的。但是列表和元組都可以進行 拼接成員運算索引和切片這些操作,就如同之前講到的字串型別一樣,因為字串就是字元按一定順序構成的序列,在這一點上三者並沒有什麼區別。我們 推薦大家使用列表的生成式語法來建立列表,它很好用,也是Python中非常有特色的語法。


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

相關文章