可變的元組
如果熟悉列表的話一定對元組不陌生,一樣是容器序列,但是列表確實可變序列,確實不可變序列,因此不少人說元組是不可變的列表。那么來看一下元組究竟能不能變
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 4312681568 ➌ 12 >>> t *= 2 13 >>> id(t) 14 4301348296 ➍
❶ 剛開始時列表的 ID。
❷ 運用增量乘法后,列表的 ID 沒變,新元素追加到列表上。
❸ 元組最開始的 ID。
❹ 運用增量乘法后,新的元組被創建。
對於列表,在+=和*= 操作之后,id沒有發生改變,說明只是在容器中追加元素而已,列表還是原來那個列表,但是對於元組來說,id值卻改變了,另外元組本身又是不可變的。其實在進行*= 或者+= 運算時,對不可變序列進行重復拼接操作的話,效率會很低,因為每次都有一個新對象,而解釋器需要把原來對象中的元素先復制到新的對象里,然后追加新的元素。也就是 t = t + t, *= 運算符創建一個新元組,然后重新綁定給變量t。