Python 元组:不可变序列与复合数据结构精讲|《Think Python》第11章
11. 元组
本章介绍 Python 内置类型元组(tuple),讲解元组的定义、不可变性、元组赋值、函数多返回值、参数打包与解包、zip 与 enumerate 用法、字典反转等核心内容。元组可与列表、字典配合使用,是实现高效算法的重要数据结构。
11.1 元组类似于列表
元组是值的序列,可以包含任意类型,用整数索引,和列表非常相似。重要区别是元组不可变。
# 用逗号分隔创建元组t = 'l', 'u', 'p', 'i', 'n'type(t)
运行结果:
tuple
# 常用括号包裹元组t = ('l', 'u', 'p', 'i', 'n')type(t)
运行结果:
tuple
# 单元素元组必须加末尾逗号t1 = 'p',type(t1)
运行结果:
tuple
# 使用 tuple() 函数创建空元组t = tuple()t
运行结果:
()
# 从序列创建元组t = tuple('lupin')t
运行结果:
('l', 'u', 'p', 'i', 'n')
# 索引访问元素t[0]
运行结果:
'l'
# 切片操作t[1:3]
运行结果:
('u', 'p')
11.2 元组是不可变的
元组不支持修改元素,因此没有 append、remove 等方法。由于不可变,元组是可哈希的,可以作为字典的键。
t = ('l', 'u', 'p', 'i', 'n')# 尝试修改元组元素会报错# t[0] = 'L'
运行结果:
TypeError: 'tuple' object does not support item assignment
# 元组可以作为字典的键d = {}d[(1, 2)] = 3d[(3, 4)] = 7d[(1, 2)]
运行结果:
3
11.3 元组赋值
赋值语句左侧可以使用元组,实现同时对多个变量赋值。右侧可以是任何序列类型。
# 同时给 a 和 b 赋值a, b = 1, 2a, b
运行结果:
(1, 2)
# 拆分邮箱地址email = 'monty@python.org'username, domain = email.split('@')username, domain
运行结果:
('monty', 'python.org')
# 直接交换两个变量,无需临时变量a, b = b, a
# 遍历字典时直接解包键值对d = {'one': 1, 'two': 2}for key, value in d.items():print(key, '->', value)
运行结果:
one -> 1two -> 2
11.4 元组作为返回值
函数严格来说只能返回一个值,但返回元组可以实现返回多个值的效果。
# divmod 一次性返回商和余数divmod(7, 3)
运行结果:
(2, 1)
# 用元组赋值接收结果quotient, remainder = divmod(7, 3)quotient
运行结果:
2
# 自定义函数返回最小值和最大值组成的元组defmin_max(t):returnmin(t), max(t)low, high = min_max([2, 4, 1, 3])low, high
运行结果:
(1, 4)
11.5 参数打包与解包
以 * 开头的参数会将多个参数打包为元组;调用时在序列前加 * 可将其解包为多个参数。
# 接收任意数量的参数,打包为元组 argsdefmean(*args):returnsum(args) / len(args)mean(1, 2, 3)
运行结果:
2.0
# 解包元组作为函数参数t = (7, 3)divmod(*t)
运行结果:
(2, 1)
# 去掉最高最低分求平均分deftrimmed_mean(*args): low, high = min_max(args) trimmed = list(args) trimmed.remove(low) trimmed.remove(high)return mean(*trimmed)trimmed_mean(1, 2, 3, 10)
运行结果:
2.5
11.6 zip 与 enumerate
zip
将多个序列按位置配对,返回由元组组成的迭代对象。
scores1 = [1, 2, 4, 5, 1, 5, 2]scores2 = [5, 5, 2, 2, 5, 2, 3]# 配对遍历for team1, team2 inzip(scores1, scores2):print(team1, team2)
运行结果:
1 52 54 25 21 55 22 3
# 用 zip 快速创建字典letters = 'abcdefghijklmnopqrstuvwxyz'numbers = range(len(letters))letter_map = dict(zip(letters, numbers))letter_map['a'], letter_map['z']
运行结果:
(0, 25)
enumerate
同时遍历序列的索引和元素。
for index, element inenumerate('abc'):print(index, element)
运行结果:
0 a1 b2 c
11.7 比较与排序
元组按元素依次比较,可用于排序。sorted 可以指定 key 函数。
(0, 1, 2) < (0, 3, 4)
运行结果:
True
# 统计字符出现次数defvalue_counts(string): counter = {}for letter in string:if letter notin counter: counter[letter] = 1else: counter[letter] += 1return countercounter = value_counts('banana')items = counter.items()
运行结果:
dict_items([('b', 1), ('a', 3), ('n', 2)])
# 按元组的第二个元素排序defsecond_element(t):return t[1]sorted_items = sorted(items, key=second_element)sorted_items
运行结果:
[('b', 1), ('n', 2), ('a', 3)]
# 直接获取计数最大的项max(items, key=second_element)
运行结果:
('a', 3)
11.8 反转字典
字典的值可能重复,因此反转字典需要将值作为新键,对应原键组成的列表作为新值。
definvert_dict(d): new = {}for key, value in d.items():if value notin new: new[value] = [key]else: new[value].append(key)return newd = value_counts('parrot')invert_dict(d)
运行结果:
{1: ['p', 'a', 'o', 't'], 2: ['r']}
11.9 调试
列表、字典、元组构成复合数据结构,容易出现结构错误。可以使用结构诊断工具查看数据形状。
# 示例:查看数据结构形状# from structshape import structshape# structshape([(1, 'a'), (2, 'b')])
11.10 术语表
11.11 练习
11.11.2 包含可变元素的元组
list0 = [1, 2, 3]list1 = [4, 5]t = (list0, list1)# 修改元组内的列表t[1].append(6)t
运行结果:
([1, 2, 3], [4, 5, 6])
# 包含列表的元组不可哈希,不能作为字典键# d = {t: '内容'}
运行结果:
TypeError: unhashable type: 'list'
11.11.3 凯撒密码
defshift_word(word, n): letters = 'abcdefghijklmnopqrstuvwxyz' letter_map = dict(zip(letters, range(len(letters)))) result = []for c in word:if c in letter_map: idx = letter_map[c] new_idx = (idx + n) % 26 result.append(letters[new_idx])else: result.append(c)return''.join(result)shift_word('cheer', 7)
运行结果:
jolly
11.11.4 按字母频率降序输出
defmost_frequent_letters(s): counter = value_counts(s) items = sorted(counter.items(), key=lambda x: x[1], reverse=True)for char, count in items:print(char, count)
11.11.5 查找变位词组
deffind_anagrams(word_list): d = {}for word in word_list: key = ''.join(sorted(word))if key notin d: d[key] = [word]else: d[key].append(word)return [group for group in d.values() iflen(group) > 1]
11.11.6 单词距离
defword_distance(w1, w2): count = 0for c1, c2 inzip(w1, w2):if c1 != c2: count += 1return count