元组(tuple)是 Python 中有序、不可变的序列类型。外观与列表相似,支持下标与切片,但创建后不能替换其中的元素。本节说明字面量与单元素写法、不可变性的含义、解包与遍历,并对照列表讨论选型。不可变记录、多返回值与可作字典键等场景常用元组。

先建一个元组

常见写法为圆括号与逗号分隔的元素。下列元组含三个整数,可通过下标访问,也可用 len 求长度:

t = (1, 2, 3)
print(t)          # (1, 2, 3)
print(t[0])       # 1
print(len(t))     # 3

元素类型可以混合。空元组写作 ()。仅含一个元素时逗号不可省略:表达式中的 (5) 仍为整数 5,须写作 (5,) 才被识别为元组。赋值时外层圆括号也可省略,逗号才是构成元组的关键:

a = ()
b = (5,)
c = (1, "hello", 3.5)
d = 1, 2, 3

print(b, d)       # (5,) (1, 2, 3)

不可变意味着什么

对某下标重新赋值,或调用列表专有的 append,将分别触发 TypeError 与 AttributeError:

t = (1, 2, 3)
# t[0] = 99    # TypeError
# t.append(4)  # AttributeError

若需调整内容,通常先 list(t) 转为列表,修改后再 tuple(...) 得到新元组,原对象未被原地改写。

不可变约束的是元组各槽位的引用,而非槽位内可变对象的内容:若某位置存放列表,仍可向该列表追加元素,元组长度与各位置引用不变。若要求整份数据不可变,应避免在元组中嵌入列表等可变对象。

下标与切片

正下标从 0 起算,负下标从右端计数,-1 为最后一项。切片区间为含头不含尾,步长为负时可得到逆序的新元组:

t = (10, 20, 30, 40, 50)
print(t[0])        # 10
print(t[-1])       # 50
print(t[1:4])      # (20, 30, 40)
print(t[::-1])     # (50, 40, 30, 20, 10)

切片结果仍为元组。元组不支持对切片区间赋值,这一点与列表不同。

元组解包

左侧多个目标与右侧一个元组按位置对应,称为解包。交换两个变量时,右侧先构成临时元组再解包至左侧。函数中 return a, b 实际返回元组,调用方可同样解包接收:

point = (3, 5)
x, y = point
print(x, y)        # 3 5

a, b = b, a
print(a, b)

def get_point():
    return 3, 5

x, y = get_point()
print(x, y)        # 3 5

左右两侧元素个数须一致,否则抛出 ValueError。元素较多时可用 * 收集剩余部分,例如 first, *mid, last = t。

遍历与成员判断

for 可遍历元组中每一项。in 用于成员检测,count 与 index 的用法与列表中的同名方法相近:

t = ("a", "b", "c")
for item in t:
    print(item, end=" ")   # a b c
print()

print("b" in t)      # True
print(t.count("a"))  # 1
print(t.index("c"))  # 2

元组和列表怎么选

特点列表 list元组 tuple
可变性可变不可变
语法中括号 []小括号 () 或省略
用途数据还会增删改当作固定记录
性能与键稍慢稍快,且可作字典键

若后续仍需增删元素,宜使用列表。若逻辑上为固定记录,或需要作为字典键,宜使用元组。元组可哈希而列表不可哈希,这是能否作为键的根本原因。

常见误区

  • 单元素元组若省略逗号,(5) 仍为整数而非元组。
  • 需要修改内容时,应经列表中转而非对下标赋值。
  • 元组内嵌可变对象时,内部对象仍可被修改,不可变仅约束元组自身结构。

本节说明了 Python 元组的创建、不可变性、解包及与列表的对比。下一篇介绍集合:元素唯一、无序及集合运算。