6. 数据类型 —— 元组
元组属于容器存储、不可变、顺序访问的数据类型。元组可以看作是不可变的列表,除了这一点外元组和列表的特征十分相似。
1. 创建元组
元组使用 ( )
来定义,直接把一个被 ( )
包含,由 ,
分隔的数据赋值给一个变量即可创建元组对象。
>>> tup = (1, 2, 3) # 通过直接将由圆括号 () 括起来的对象赋值给变量创建元组
>>> tup
(1, 2, 3)
>>> tup = (1, ) # 如果元组对象中只有一个元素时,必须在元素后添加一个 ',' 否则不会创建元组
>>> tup
(1, )
>>> tup = (1, 'a', [1]) # 和列表一样,元组的元素可以是任何数据类型
>>> tup
(1, 'a', [1])
元组同样可以使用 tuple()
函数来创建元组对象
>>> tup = tuple([1, 2, 3])
>>> tup
(1, 2, 3)
2. 元组的基本操作
-
访问元组中的元素
元素属于顺序访问的序列类型,因此可以通过索引和切片操作对元组中的元素进行访问
tup = (1, 2, 3, 4, 5, 6, 7)
tup[0] # 元组索引和字符串一样,从 0 开始,最大为列表的长度 - 1
1
tup[-1] # 使用负索引访问元组元素
7
tup[1:3] # 元组的切片操作返回一个包含元组部分元素的新的元组
(2, 3)
tup[::-1] # 反序访问元组
(7, 6, 5, 4, 3, 2, 1)
tup = ([1, 2, 3],)
tup[0][1] # 元组中嵌套的任意序列数据类型的访问,注意元组中只有一个元素时,元素后边要加 ','
2
```
-
元组不可变性
元组中的元素一般来说是不可以改变的,但由于元组中可以包含列表、字典等其他数据类型,而这些数据类型中的元素是可以改变的,因此在这种情况下元组发生了 "改变"。元组的不可变性仅适用于元组本身元素顶层,而非其元素的内容
>>> tup = ([1, 2, 3], )
>>> tup[0][1] = 7 # 改变元组中列表元素的值,可以在 "改变" 元组
>>> tup
([1, 7, 3])
>>> tup[0] = 1 # 如果直接更改元组的元素会引发一个 TypeError 异常
Traceback (most recent call last):
File "<stdin>", line 1, in <module>
TypeError: 'tuple' object does not support item assignment
3. 元组的一些操作符和函数
元组属于序列数据类型,因此大部分序列操作和函数适用于元组,但由于元组的不可变性,一些需要对序列对中元素进行修改的函数并未对元组进行实现。
操作符
-
+
操作符:对元组使用 + 操作符可以将两个元组连接在一起创建一个新的元组
(1, 2) + (3, 4)
(1, 2, 3, 4) # 直接生成一个新的元组而并非对原元组进行修改
```
-
*
操作符:使用 * 操作符可以将元组重复 N 次
(1, ) * 7
(1, 1, 1, 1, 1, 1, 1)
```
-
in
和not in
:成员关系操作,测试对象是否是元组的元素
tup = (1, 2, 3)
1 not in tup
False
1 in tup
True
```
函数
元组很像列表,但由于元组的不可变性,因此在进行绝大多数函数操作时,首先将元组转换为列表,然后继续后边的操作。
>>> tup = (3, 1, 2)
>>> li = list(tup) # 首先将元组转换为列表
>>> li.sort() # 对列表进行排序
>>> tup = tuple(li) # 将排好序的列表转化为元组
>>> tup
(1, 2, 3)
元组仅有的两个方法 index
和 count
,这两个方法对元组跟对列表一样
>>> tup = (1, 2, 3, 1)
>>> tup.index(1)
0
>>> tup.count(1)
2
4. 元组的意义
元组的意义在于它的不可变性。一个元组对象的值不会发生改变,也就意味着它们可以通过 hash 算法得到的值时固定的。因此元组可以作为字典的键。这是元组的一个典型应用场景,通常来说,元组用在需要确保数据不会被修改的时候。
参考:
Tuple
复制对象 —— 浅复制和深复制
当我们为变量进行赋值时,只是创建了对对象的引用,而不是将对象进行复制。当将其赋值给另外一个变量时,也并没有复制这个对象,而是复制了引用。
>>> a = 1
>>> id(a)
8109160
>>> b = a
>>> id(b)
8109160 # 变量 a 和变量 b 指向同一个对象
使用切片操作和函数进行对象的复制
>>> li = [1, [2, 3]]
>>> a = li[:] # 使用切片操作对 li 进行复制
>>> b = list(li) # 使用 list() 函数对 li 进行复制
>>> a[0] = 7
>>> a
[7, [2, 3]] # 修改了 a 的第一个元素
>>> b
[1, [2, 3]] # 并没有对 b 造成改变
>>> a[1][0] = 8
>>> a
[7, [8, 3]] # 将 a 中的列表元素中的元素值进行修改
>>> b
[1, [8, 3]] # b 中列表元素同样发生了改变
发生上述情况的原因在于浅复制。对一个对象进行浅复制创建了一个新的对象,但对象的内容是对原来对象内容的引用。
>>> id(a[1])
139969576313056
>>> id(b[1])
139969576313056 # 变量 a 和 b 对应的列表元素指向同一个对象,因此同时发生了改变
>>> id(a[0])
8109016
>>> id(b[0])
8109160 # 由于数字、字符串等是不可变类型,因此改变该元素即赋值了一个新的对象
使用浅复制的方法:1)切片操作 [:];2)list()、dict() 等函数;3)使用 copy 模块中的 copy 函数
如果需要得到一个完全不同的复制,需要使用 copy 的 copy.deepcopy() 函数,即深复制
>>> import copy
>>> a = li
>>> b = copy.deepcopy(li)
>>> id(a[1])
139969576313056
>>> id(b[1])
139969576369112 # a、b 中的列表元素为不同的引用
- 浅复制和深复制只是针对除元组以外的容器数据类型。