元組-不僅僅是不可變的列表


可變的元組

如果熟悉列表的話一定對元組不陌生,一樣是容器序列,但是列表確實可變序列,確實不可變序列,因此不少人說元組是不可變的列表。那么來看一下元組究竟能不能變

tup = (1, 2, 3, [4, 5])
tup[3] += [6, 7]

如果執行這個操作將會出現什么

TypeError: 'tuple' object does not support item assignment

而tpl[3].extend[6,7 ]卻不會出錯

結果報錯

但是當我們打印tup時卻又發現

(1, 2, 3,[4, 5, 6])

這是因為tup元組在下標為3的位置其實是對列表[4, 5]所在內存空間的引用,你也可以理解為是一個指向內存中的指針,當你對tpl[3]進行“+= [ 6, 7 ]”操作的時候其實是對[4, 5]列表對象進行“.extend([6, 7])”操作,所以操作成功。

記錄:

元組其實是對數據的記錄:元組中的每個元素都存放了記錄中一個字段的數據,外加這個字段的位置。正是這個位置信息給數據賦予了意義。

來看下面例子

>>> lax_coordinates = (33.9425, -118.408056) ➊
>>> city, year, pop, chg, area = ('Tokyo', 2003, 32450, 0.66, 8014) ➋
>>> traveler_ids = [('USA', '31195855'), ('BRA', 'CE342567'), ➌
... ('ESP', 'XDA205856')]
>>> for passport in sorted(traveler_ids): ➍
... print('%s/%s' % passport) ➎
...
BRA/CE342567
ESP/XDA205856
USA/31195855
>>> for country, _ in traveler_ids: ➏
... print(country)
...
USA
BRA
ESP

❶ 洛杉磯國際機場的經緯度。
❷ 東京市的一些信息:市名、年份、人口(單位:百萬)、人口變化(單位:百分比)和面積(單位:平方千米)。
❸ 一個元組列表,元組的形式為 (country_code,passport_number)。
❹ 在迭代的過程中,passport 變量被綁定到每個元組上。
❺ % 格式運算符能被匹配到對應的元組元素上。
❻ for 循環可以分別提取元組里的元素,也叫作拆包(unpacking)。因為元組中第二個元素對我們沒有什么用,所以它賦值給“_”占位符。
元組拆包

就像 ➋中的city, year, pop, chg, area = ('Tokyo', 2003, 32450, 0.66, 8014),只要這一條表達式就可以將一條記錄賦值給多個變量,同樣('%s/%s' % passport) 也是對元組拆包的應用。元組拆包也有他的規矩,那就是可迭代的對象(元組)的元素數量必須要接受這些元素的空檔數一致。除非用“ * ”來接受多余的元素。

>>> lax_coordinates = (33.9425, -118.408056)
>>> latitude, longitude = lax_coordinates # 元組拆包
>>> latitude
33.9425
>>> longitude
-118.408056

優雅的元組拆包

實現變量值的互換

a, b = b, a

元組拆包在冒泡排序中的應用

def bubbleSort(alist):
    for passnum in range(len(alist)-1, 0, -1):
        for i in range(passnum):
            if alist[i]>alist[i+1]:
#                 temp = alist[i]
#                 alist[i] = alist[i+1]
#                 alist[i+1] = temp
                alist[i+1],alist[i] = alist[i], alist[i+1]  #這就是元組拆包的優秀
                
alist = [54,26,93,17,77,31.0,31,44,55,20]
bubbleSort(alist)
print(alist)

使用“*”

(1)可以用 * 運算符把一個可迭代對象拆開作為函數的參數:

>>> divmod(20, 8)
(2, 4)
>>> t = (20, 8)
>>> divmod(*t)
(2, 4)
>>> quotient, remainder = divmod(*t)
>>> quotient, remainder
(2, 4)

(2)上面說用*來接受多余的元素,在寫函數時,我們也經常用*arg來獲取不確定數量的元素

>>> a, b, *rest = range(5)
>>> a, b, rest
(0, 1, [2, 3, 4])
>>> a, b, *rest = range(3)
>>> a, b, rest
(0, 1, [2])
>>> a, b, *rest = range(2)
>>> a, b, rest
(0, 1, [])

(3)也可以出現在拆包中的任何位置

>>> a, *body, c, d = range(5)
>>> a, body, c, d
(0, [1, 2], 3, 4)
>>> *head, b, c, d = range(5)
>>> head, b, c, d
([0, 1], 2, 3, 4)

 小知識

上面第一節說到,元組的不可變性是相對的,即我們可以改變元組內對其他對象的引用,但是我們知道,在可變序容器列表中,我們可以使用“*+”,或者“+=”,來改變當前列表中的元素,那么對於不可變容器序列元組呢?

來看下面例子:

 1 >>> l = [1, 2, 3]
 2 >>> id(l)
 3 4311953800 4 >>> l *= 2
 5 >>> l
 6 [1, 2, 3, 1, 2, 3]
 7 >>> id(l)
 8 4311953800 9 >>> t = (1, 2, 3)
10 >>> id(t)
11 431268156812 >>> t *= 2
13 >>> id(t)
14 4301348296 ➍

❶ 剛開始時列表的 ID。
❷ 運用增量乘法后,列表的 ID 沒變,新元素追加到列表上。
❸ 元組最開始的 ID。
❹ 運用增量乘法后,新的元組被創建。

對於列表,在+=和*= 操作之后,id沒有發生改變,說明只是在容器中追加元素而已,列表還是原來那個列表,但是對於元組來說,id值卻改變了,另外元組本身又是不可變的。其實在進行*= 或者+= 運算時,對不可變序列進行重復拼接操作的話,效率會很低,因為每次都有一個新對象,而解釋器需要把原來對象中的元素先復制到新的對象里,然后追加新的元素。也就是  t = t + t, *= 運算符創建一個新元組,然后重新綁定給變量t。


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM