课程 22:元组与排序

学习目标

1. 元组基础与进阶

1.1 什么是元组?

定义: 元组(tuple)是不可变的有序序列,用小括号 () 表示。元素类型不限,支持嵌套。
与列表区别: 元组不可变,适合存储只读数据,可作为字典键和集合元素。

1.2 元组的定义与访问

# 定义元组
t = (1, 2, 3)
print(t[0])
# 单元素元组
t1 = (5,)
# 嵌套元组
t2 = (1, (2, 3), 4)
print(t2[1][0])
常用操作:
• 访问元素:t[i]
• 切片:t[1:3]
• 遍历:for x in t

1.3 元组的解包与多返回值

# 多变量赋值
x, y, z = (1, 2, 3)
# 星号解包
head, *body, tail = (1, 2, 3, 4, 5)
print(head, body, tail)  # 1 [2, 3, 4] 5
# 函数多返回值
def min_max(lst):
    return min(lst), max(lst)
lo, hi = min_max([3, 1, 4])
print(lo, hi)
解包可用于交换变量、函数参数、循环遍历等。

1.4 namedtuple与类型注解

from collections import namedtuple
Point = namedtuple('Point', ['x', 'y'])
p = Point(3, 4)
print(p.x, p.y)
# 类型注解
from typing import Tuple
point: Tuple[int, int] = (3, 4)
namedtuple适合结构化只读数据,类型注解提升代码可读性和静态检查。

1.5 元组的hash特性与不可变性安全

t = (1, 2, 3)
print(hash(t))
# 元组可作为dict的key,list不行
d = {(1,2): 'a'}
print(d[(1,2)])
# t[0] = 10  # TypeError
元组元素必须全为可hash类型,才能作为字典键或集合元素。

1.6 元组与list互转、内存与性能

lst = [1,2,3]
t = tuple(lst)
lst2 = list(t)
import sys
print(sys.getsizeof(lst), sys.getsizeof(t))
元组通常比列表更省内存,且不可变更安全。

1.7 tuple vs list vs set vs dict

特性tuplelistsetdict
可变性不可变可变可变可变
有序性有序有序无序3.7+有序
可哈希
用途只读/键通用去重映射
tuple适合不可变、只读、作为key,list适合频繁增删改,set适合去重,dict适合映射。

2. 元组的不可变性与应用

2.1 不可变性

t = (1, 2, 3)
# t[0] = 10  # TypeError: 'tuple' object does not support item assignment
注意: 元组本身不可变,但如果元素是可变对象(如列表),其内容可变。
t = ([1,2], 3)
t[0][0] = 99
print(t)  # ([99, 2], 3)

3. 排序详解与算法原理

3.1 sorted与sort

nums = [3, 1, 4, 1, 5]
print(sorted(nums))  # 返回新列表
nums.sort(reverse=True)
print(nums)  # 原地排序
Python排序算法为Timsort,稳定且高效,复杂度O(n log n)。

3.2 key参数与自定义排序

words = ['apple', 'banana', 'pear', 'grape']
print(sorted(words, key=len))  # 按长度排序
# 排序元组列表
students = [('Alice', 95), ('Bob', 88), ('Tom', 90)]
print(sorted(students, key=lambda x: x[1], reverse=True))
常用技巧:
• key参数指定排序依据
• reverse=True降序排序
• 支持任意可迭代对象

3.3 operator模块与itemgetter

from operator import itemgetter
students = [('Alice', 95), ('Bob', 88), ('Tom', 90)]
print(sorted(students, key=itemgetter(1), reverse=True))
itemgetter比lambda更快,适合大数据量排序。

3.4 多级排序与分组聚合

# 按成绩降序,成绩相同按姓名升序
students = [('Alice', 95), ('Bob', 88), ('Tom', 95)]
result = sorted(students, key=lambda x: (-x[1], x[0]))
print(result)
# 分组聚合
grouped = {}
for name, score in students:
    grouped.setdefault(score, []).append(name)
print(grouped)
分组统计常用于数据分析,groupby需先排序。

3.5 排序性能分析与大数据处理

import random, time
lst = [random.randint(0, 100000) for _ in range(10**6)]
start = time.time()
sorted(lst)
print('耗时:', time.time()-start)
大数据排序建议用numpy、pandas等库,或分块处理。

4. 实际应用案例与可视化

4.1 元组作为字典键

d = {('Alice', 'Math'): 95, ('Bob', 'Math'): 88}
print(d[('Alice', 'Math')])

4.2 数据分析与可视化

import matplotlib.pyplot as plt
scores = [('Alice', 95), ('Bob', 88), ('Tom', 90)]
names = [x[0] for x in scores]
values = [x[1] for x in scores]
plt.bar(names, values)
plt.show()
元组常用于pandas、numpy等数据分析库的数据结构。

4.3 真实项目案例

# 经纬度坐标存储与排序
gps_points = [(39.9, 116.4), (31.2, 121.5), (22.5, 114.1)]
gps_points.sort(key=lambda x: x[0])
# 复杂数据分组与排名
from collections import defaultdict
scores = [('Alice', 'A', 95), ('Bob', 'B', 88), ('Tom', 'A', 90)]
grouped = defaultdict(list)
for name, cls, score in scores:
    grouped[cls].append((name, score))
for cls, items in grouped.items():
    print(cls, sorted(items, key=lambda x: -x[1]))
项目实战:用元组和排序实现学生成绩分析、地理信息管理、日志分析等。

5. 常见错误、边界案例与调试

# 单元素元组
x = (5)      # int
y = (5,)     # tuple
# 嵌套可变对象
t = ([1,2], 3)
t[0][0] = 99
# 排序key类型不一致
# sorted([(1,2), (3,'a')], key=lambda x: x[1])  # TypeError
调试建议:用type()、isinstance()、print()、pprint()、logging等工具定位问题。

6. 编程练习与挑战

练习1:基础操作

练习2:进阶技巧

练习3:实战项目

7. 综合作业与项目

作业22:元组与排序综合应用

任务1:学生成绩分析系统

实现一个系统,支持录入多个学生的多门课程成绩,按不同科目和总分排序,输出排名。

任务2:地理信息管理系统

实现一个地理信息管理系统,支持元组存储坐标、排序、查找最近点等功能。

任务3:数据分组与多级排序

实现一个数据分组和多级排序工具,支持按多个字段分组和排序。

任务4:数据可视化与性能分析

用matplotlib等工具对排序结果进行可视化,并分析不同排序方法的性能。

学习建议:多查官方文档,关注元组/排序的底层原理与最佳实践,遇到性能瓶颈时考虑numpy/pandas等工具。
思考题:
拓展阅读: