1. 元组基础
元组跟列表 (list) 非常相似。
- 使用小括号
()定义里面用逗号分隔开,然后()可以省略,但是逗号至少要有一个。 最后一个元素后面可以加逗号,也可以不加,但是只有一个元素的时间必须加,要不然就没一个, - 类型:
<class 'tuple'> - 空元组:
t = ()或t = tuple()。空元组的布尔值为False。 - 特性:元组属于可迭代对象,可以使用
for循环遍历,也可以使用in或not in判断元素是否包含在元组内。
# 拥有4个元素的元组对象
t1 = (55, 44, 33, 22)
print(type(t1)) # <class 'tuple'>
print(len(t1)) # 4
# 使用索引访问元素和切片
print(t1[0], t1[-1]) # 55 22
print(t1[0:2]) # (55, 44)
print(t1[0:4:2]) # (55, 33)
# 空元组
t2 = ()
t3 = tuple()
print(bool(t2)) # False
# 元组的加法和乘法
t1 = (55, 44)
t2 = (33, 22)
t3 = t1 + t2 # (55, 44, 33, 22)
t5 = t1 * 3 # (55, 44, 55, 44, 55, 44)
# 包含关系
t6 = (55, 44, 33, 44)
print(44 in t6) # True
print(11 not in t6) # True
定义元组可以省略括号:
t1 = 18, 'k66', 3.14
t2 = (18, 'k66', 3.14)
t3 = 18, 'k66', 3.14,
t4 = (18, 'k66', 3.14, )
# 以上四种方式定义的都是元组 (18, 'k66', 3.14)
定义单元素元组时,必须在元素后面加上逗号 ,,否则括号会被当作普通的数学运算符。
t1 = (55)
print(t1, type(t1)) # 55 <class 'int'>
t3 = (55,)
print(t3, type(t3)) # (55,) <class 'tuple'>
t6 = 55,
print(t6, type(t6)) # (55,) <class 'tuple'>
t4 = (55) * 3
print(t4, type(t4)) # 165 <class 'int'>
t5 = (55,) * 3
print(t5, type(t5)) # (55, 55, 55) <class 'tuple'>
2. 元组推导式
使用 ( ) 包含的推导式生成的不是元组,而是生成器(Generator)。如果要生成元组,需要使用 tuple() 函数。比方说 tuple(i for i in range(5))
# 这是一个生成器
t1 = (i for i in range(5))
print(type(t1)) # <class 'generator'>
# 生成元组
t2 = tuple(i for i in range(5))
print(t2) # (0, 1, 2, 3, 4)
print(type(t2)) # <class 'tuple'>
3. 元组的解包 (Unpacking)
元组可以通过解包将元素赋值给多个变量。age, name, male = (20, 'MJ', True)
t = (20, 'MJ', True)
age, name, male = t
print(age, name, male) # 20 MJ True
# divmod() 返回包含商和余数的元组
t = divmod(13, 4) # (3, 1)
div, mod = divmod(13, 4)
# 交换两个变量的值
a, b = b, a
# 列表也可以进行解包
a, b, c = [55, 44, 33]
字符串格式化和 enumerate 中,实际上也经常使用元组:
name = 'MJ'
age = 18
print('我的名字是%s,今年%d岁' % (name, age)) # (name, age)就是一个元组
s = [55, 44, 33, 22, 11]
# 这里的 i,e 就是元祖
for i, e in enumerate(s): # enumerate解包
print(i, e)
4. 序列类型 (Sequence Type)
list、tuple、range、str 都属于序列类型,它们有很多类似的序列操作:
list是可变类型tuple、range、str是不可变类型
常用序列操作包括:
len(s): 获取长度s1 + s2: 拼接(range不支持)s * 3: 重复(range不支持)s[i]: 获取指定索引的元素s[i:j]: 获取切片x in s: 判断包含关系s.index(x): 获取x的索引s.count(x): 统计x的数量
序列类型之间的转换
可以通过 list()、tuple() 相互转换:
# 元组转列表
s1 = list((55, 44, 33)) # [55, 44, 33]
# 列表转元组
s2 = tuple([55, 44, 33]) # (55, 44, 33)
# 字符串转列表/元组
s3 = list('mj666') # ['m', 'j', '6', '6', '6']
s4 = tuple('mj666') # ('m', 'j', '6', '6', '6')
5. 元组的不可变性
- 元组是不可变类型,没有
append、insert、extend、pop、remove、clear等方法。 - 支持
index、count方法。 - 不能修改或删除元组中的元素(如
t[0] = 66或del t[1]会报错TypeError)。 - 注意:如果元组中包含了可变类型的对象(如列表),元组本身不可修改,但该可变对象内部的元素是可以修改的。这个有点像js这种const修饰的对象不可修改,但是对象的属性如果是一个对象,这个属性对象可以修改一样
t = (55, 44, [66, 77, 88], 33)
# t[1] = 11 # 会报错
t[2][2] = 99 # 允许修改元组内部列表的元素
print(t) # (55, 44, [66, 77, 99], 33)
tuple 和 list 的选择
tuple属于不可变类型,数据相对更安全,资源消耗也会更低。- 建议:能用
tuple的地方尽量使用tuple,实在不行再使用list。(例如用month in (3, 4, 5)代替month in [3, 4, 5])
6. list、tuple、 str 的加法和乘法 (+= 和 *=)
列表的就地运算
对于可变的 list,+= 和 *= 可以实现原地运算(inplace),不会生成新的列表对象,性能更好。
s = [11, 22]
print(id(s))
s += [33, 44] # 等价于 s.extend([33, 44])
print(id(s)) # id 保持不变
s += t 的本质是执行了 s.extend(t),t 只要是可迭代对象即可。
s2 = [55, 44]
s2 += range(5) # [55, 44, 0, 1, 2, 3, 4]
字符串和元组的运算
由于 str、tuple 是不可变类型,它们的 +=、*= 无法实现原地操作,每次都会生成一个新的对象。
s1 = '小码哥'
s1 += 'MJ' # 创建了一个新字符串对象,id 发生改变
t2 = (11, 22)
t2 += (33, 44) # 创建了一个新元组对象,id 发生改变
# 注意类型匹配:元组加法必须和元组进行
t1 = (55, 44)
# t1 += [33, 22] # TypeError: can only concatenate tuple (not "list") to tuple