大家好,我是沉默小皮,让大家更容易的学习Python3,今天我们来看下:Python3 元组:不可变的“列表”,同样强大!
前面我们学习了列表,知道它是一个非常灵活的“万能容器”。今天要讲的元组(Tuple),可以说是列表的“孪生兄弟”,但有一个关键区别:元组一旦创建,就不能修改了。
这个“不可变”的特性,让元组在一些特定场景下比列表更安全、更高效。
一、元组是什么?怎么创建?
元组与列表非常相似,不同之处在于:
- 元组使用**小括号
()**,列表使用方括号 []
# 创建元组的几种方式
tup1 = ('Google', 'XiaoPi', 1997, 2000) # 标准写法
tup2 = (1, 2, 3, 4, 5) # 纯数字元组
tup3 = "a", "b", "c", "d"# 不加括号也可以!Python会识别为元组
empty_tup = () # 空元组
print(type(tup3)) # <class 'tuple'>
⚠️ 特别注意:单元素元组必须加逗号
这是新手最容易踩的坑:当元组只有一个元素时,必须在元素后面加一个逗号,否则括号会被当作普通运算符。
# 错误示范:不加逗号,结果是整数,不是元组
tup_wrong = (50)
print(type(tup_wrong)) # <class 'int'>
# 正确示范:加逗号
tup_correct = (50,)
print(type(tup_correct)) # <class 'tuple'>
# 甚至可以不写括号,但逗号必须有
tup2 = 50,
print(type(tup2)) # <class 'tuple'>
二、访问元组元素:索引与切片
元组和列表一样,支持通过索引和切片来访问元素。
tup1 = ('Google', 'XiaoPi', 1997, 2000)
tup2 = (1, 2, 3, 4, 5, 6, 7)
# 通过索引访问
print(tup1[0]) # Google
print(tup2[1:5]) # (2, 3, 4, 5) 注意结果是元组
# 负数索引
print(tup1[-1]) # 2000(最后一个元素)
# 切片步长
print(tup2[::2]) # (1, 3, 5, 7)
三、元组的“修改”与“删除”
3.1 不能修改单个元素
元组的不可变指的是:不能修改、添加或删除其中的单个元素。
tup = ('r', 'u', 'n', 'o', 'o', 'b')
# 下面这行会报错!
# tup[0] = 'g'
# TypeError: 'tuple' object does not support item assignment
3.2 但可以拼接生成新元组
虽然不能修改原元组,但可以通过 + 或 += 运算生成一个新的元组。
tup1 = (12, 34.56)
tup2 = ('abc', 'xyz')
# 拼接生成新元组
tup3 = tup1 + tup2
print(tup3) # (12, 34.56, 'abc', 'xyz')
# 也可以使用 +=
tup1 += tup2
print(tup1) # (12, 34.56, 'abc', 'xyz')
理解关键:tup1 += tup2 并不是修改了原来的 tup1,而是创建了一个新元组,然后把新元组赋值给 tup1 这个变量名。原来的元组如果没有其他变量引用,就会被垃圾回收。
3.3 可以删除整个元组
虽然不能删单个元素,但可以用 del 语句删除整个元组变量。
tup = ('Google', 'XiaoPi', 1997, 2000)
print(tup)
del tup # 删除元组变量
# print(tup) # 这行会报错:NameError: name 'tup' is not defined
四、元组运算符
元组支持的操作符与列表类似:
| | |
|---|
len((1,2,3)) | 3 | |
(1,2,3) + (4,5,6) | (1,2,3,4,5,6) | |
('Hi!',) * 4 | ('Hi!','Hi!','Hi!','Hi!') | |
3 in (1,2,3) | True | |
for x in (1,2,3): print(x) | 1 2 3 | |
a = (1, 2, 3)
b = (4, 5, 6)
# 连接
c = a + b
print(c) # (1, 2, 3, 4, 5, 6)
# 重复
print(('Hi!',) * 3) # ('Hi!', 'Hi!', 'Hi!')
# 成员判断
print(2in a) # True
五、元组的索引与切片(详细)
定义一个元组,演示各种切片操作:
tup = ('Google', 'XiaoPi', 'Taobao', 'Wiki', 'Weibo', 'Weixin')
# 索引
print(tup[0]) # Google(第一个)
print(tup[1]) # XiaoPi(第二个)
print(tup[-1]) # Weixin(最后一个)
print(tup[-2]) # Weibo(倒数第二个)
# 切片
print(tup[1:]) # ('XiaoPi', 'Taobao', 'Wiki', 'Weibo', 'Weixin')
print(tup[1:4]) # ('XiaoPi', 'Taobao', 'Wiki')(索引1到3)
print(tup[::2]) # ('Google', 'Taobao', 'Weibo')(步长2)
六、常用内置函数
| | |
|---|
len(tuple) | | len((1,2,3)) |
max(tuple) | | max((5,4,8)) |
min(tuple) | | min((5,4,8)) |
tuple(iterable) | | tuple([1,2,3]) |
# 将列表转换为元组
my_list = ['Google', 'Taobao', 'XiaoPi', 'Baidu']
my_tuple = tuple(my_list)
print(my_tuple) # ('Google', 'Taobao', 'XiaoPi', 'Baidu')
# 将字符串转换为元组
print(tuple("hello")) # ('h', 'e', 'l', 'l', 'o')
七、深入理解“不可变”
很多新手会困惑:为什么下面这个例子中,tup 的值好像“变了”?
tup = ('r', 'u', 'n', 'o', 'o', 'b')
print(id(tup)) # 比如输出:4440687904
tup = (1, 2, 3)
print(id(tup)) # 输出:4441088800(地址变了!)
关键理解:这里的 tup = (1, 2, 3) 并不是修改原来的元组,而是创建了一个全新的元组对象,然后把变量名 tup 重新指向这个新对象。原来的元组 ('r', 'u', 'n', 'o', 'o', 'b') 并没有被修改,只是失去了 tup 这个“标签”。
元组的“不可变”指的是:一旦元组对象在内存中被创建,它的内容就不能被改变。你无法像列表那样 tup[0] = 'g'。
八、什么时候用元组而不是列表?
# 元组作为字典的键(列表不行)
locations = {
(40.7128, -74.0060): "纽约",
(34.0522, -118.2437): "洛杉矶"
}
# 函数返回多个值(本质上是元组)
defget_user():
name = "沉默小皮"
age = 28
return name, age # 实际返回 (name, age)
result = get_user()
print(result[0]) # 沉默小皮
print(result[1]) # 28
💡 几点真实经验与避坑建议
单元素元组的逗号不能省
(5) 是整数,(5,) 才是元组。这是新手最常见的错误之一。
元组的不可变是“内容不可变”,不是变量名不可变
你可以重新赋值一个新的元组给同一个变量名,但你不能修改原元组的内容。
元组比列表更节省内存
如果你的数据不需要修改,使用元组可以获得更好的性能,因为Python对元组的优化比列表更激进。
小心元组里的可变元素
虽然元组本身不可变,但如果元组里包含一个列表,这个列表的内容是可以被修改的!因为元组只“管”它的直接元素,不管元素内部的变化。
tup = (1, 2, [3, 4])
tup[2].append(5) # 可以执行!tup 变成 (1, 2, [3, 4, 5])
# 但 tup[2] = [6,7] 仍然会报错
用元组做函数参数可以避免意外修改
如果你写了一个函数,不希望调用者传入的列表被意外修改,可以把参数类型改为元组,或者在函数内部先转换成元组。
元组的拼接和重复会创建新对象
频繁使用 + 或 * 操作大量元组时,会不断创建新对象,注意性能开销。如果确实需要频繁操作,考虑转成列表。