可变的元组
如果熟悉列表的话一定对元组不陌生,一样是容器序列,但是列表确实可变序列,确实不可变序列,因此不少人说元组是不可变的列表。那么来看一下元组究竟能不能变
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。