CHAPTER 03

数据结构:列表、字典、元组与集合

Python 的容器类型,用于组织和存储多个数据。AI 中的训练数据、配置参数、模型结果,都用这些结构来管理。

list —— 列表(有序可变)

列表是 Python 最常用的数据结构。用方括号 [] 定义,可以存任意类型的数据,有顺序、可修改

核心概念

索引(Index)从 0 开始

列表中每个元素都有一个位置编号,从 0 开始(不是 1)。第一个元素是 list[0],第二个是 list[1]。Python 还支持负数索引list[-1] 是最后一个元素,非常方便。

PYTHON · list 基础
# 创建列表
losses = [2.5, 1.8, 1.2, 0.9, 0.7]  # 每轮的损失值
labels = ["cat", "dog", "bird"]       # 分类标签
mixed = [1, "hello", 3.14, True]     # 可以存不同类型

# 索引访问
print(losses[0])   # 2.5(第一个)
print(losses[2])   # 1.2(第三个)
print(losses[-1])  # 0.7(最后一个)
print(losses[-2])  # 0.9(倒数第二个)

# 切片(slice):取一段数据
print(losses[1:3])  # [1.8, 1.2](索引1到2,不含3)
print(losses[:3])   # [2.5, 1.8, 1.2](前3个)
print(losses[2:])   # [1.2, 0.9, 0.7](从第3个到结束)
print(losses[::-1]) # [0.7, 0.9, 1.2, 1.8, 2.5](反转)
PYTHON · list 操作方法
history = [2.5, 1.8]  # 训练历史

# 添加元素
history.append(1.2)      # 末尾添加:[2.5, 1.8, 1.2]
history.insert(0, 3.0)   # 指定位置插入:[3.0, 2.5, 1.8, 1.2]
history.extend([0.9, 0.7]) # 合并另一个列表

# 删除元素
history.remove(3.0)   # 删除第一个值为3.0的元素
last = history.pop()  # 弹出并返回最后一个元素

# 查询
print(len(history))         # 元素数量
print(min(history))         # 最小值(最好的 loss)
print(max(history))         # 最大值
print(sum(history))         # 求和
print(sum(history)/len(history))  # 平均值

# 排序
history.sort()              # 原地排序(升序)
history.sort(reverse=True) # 降序
sorted_h = sorted(history) # 返回新列表,不修改原列表

# 检查元素是否在列表中
print(0.9 in history)       # True
🤖

AI 中的列表

在 AI 开发中,列表无处不在:train_losses = [] 记录训练损失曲线,messages = [{"role": "user", "content": "..."}] 构建对话历史,predictions = [] 收集模型预测结果。

列表推导式(List Comprehension)

这是 Python 的特色语法,可以用一行代码从现有列表创建新列表,比循环更简洁。在数据预处理中大量使用。

PYTHON · 列表推导式
# 普通写法(循环)
squares = []
for x in range(5):
    squares.append(x ** 2)

# 列表推导式(一行)= [表达式 for 变量 in 可迭代对象]
squares = [x ** 2 for x in range(5)]
print(squares)  # [0, 1, 4, 9, 16]

# 带条件筛选
even = [x for x in range(10) if x % 2 == 0]
print(even)     # [0, 2, 4, 6, 8]

# AI 实战:归一化数据到 [0, 1]
raw_scores = [10, 20, 30, 40, 50]
max_score = max(raw_scores)
normalized = [s / max_score for s in raw_scores]
print(normalized)  # [0.2, 0.4, 0.6, 0.8, 1.0]

# 批量处理文本(NLP 预处理)
texts = ["Hello World", "  Python  ", "AI Dev"]
cleaned = [t.strip().lower() for t in texts]
print(cleaned)  # ['hello world', 'python', 'ai dev']

dict —— 字典(键值对映射)

字典用 {} 定义,存储键(key)→ 值(value)的映射关系。就像真实的字典:通过词条(key)找到解释(value)。在 AI 开发中,字典是最重要的数据结构之一,API 请求、配置参数、模型结果都用字典表示。

PYTHON · dict 基础
# 创建字典:{ key: value, key: value, ... }
model_config = {
    "model": "claude-sonnet-4-6",
    "max_tokens": 1024,
    "temperature": 0.7,
    "stream": False
}

# 访问值(用 key 取 value)
print(model_config["model"])        # claude-sonnet-4-6
print(model_config["temperature"])  # 0.7

# 安全访问(不存在的 key 用 get,不会报错)
print(model_config.get("timeout"))         # None(key不存在)
print(model_config.get("timeout", 30))    # 30(设置默认值)

# 添加/修改
model_config["top_p"] = 0.9         # 添加新键
model_config["temperature"] = 0.5   # 修改已有键

# 删除
del model_config["stream"]          # 删除 key
val = model_config.pop("top_p")    # 删除并返回值

# 查询
print("model" in model_config)      # True(检查 key 是否存在)
print(model_config.keys())          # 所有 key
print(model_config.values())        # 所有 value
print(model_config.items())         # 所有 (key, value) 对
PYTHON · AI 场景中的字典
# LLM API 消息格式(最常见的字典用法)
messages = [
    {"role": "system", "content": "你是一个 Python 专家"},
    {"role": "user", "content": "如何学习 Python"}
]

# 模型训练结果统计
metrics = {
    "train_loss": 0.23,
    "val_loss": 0.31,
    "accuracy": 0.94,
    "f1_score": 0.92
}

# 遍历字典
for metric_name, value in metrics.items():
    print(f"{metric_name}: {value}")
# train_loss: 0.23
# val_loss: 0.31 ...

# 字典推导式
raw = {"a": 1, "b": 2, "c": 3}
doubled = {k: v * 2 for k, v in raw.items()}
print(doubled)  # {'a': 2, 'b': 4, 'c': 6}

# 合并字典(Python 3.9+)
base_config = {"model": "gpt-4o", "temperature": 0.7}
extra = {"max_tokens": 2048}
full_config = base_config | extra  # 合并

tuple —— 元组(有序不可变)

元组用圆括号 () 定义,和列表类似,但不能修改(不可变)。一旦创建,元素就固定了。适合存储不应该被改变的数据,比如坐标、RGB 颜色值、图像尺寸。

PYTHON · tuple 元组
# 创建元组
image_size = (224, 224, 3)   # (高, 宽, 通道) 图像尺寸
point = (3.5, 2.1)          # 2D 坐标
rgb = (255, 128, 0)          # 橙色的 RGB 值

# 访问元素(和列表一样用索引)
print(image_size[0])  # 224(高度)
print(image_size[2])  # 3(通道数)

# 元组不能修改(这行会报错!)
# image_size[0] = 256  ← TypeError!

# 解包(unpack):把元组拆分成多个变量
height, width, channels = image_size
print(f"图像: {height}x{width}, {channels}通道")

# 函数返回多个值时,实际上是返回一个元组
def get_model_stats():
    return 0.95, 0.23  # 返回 (accuracy, loss)

acc, loss = get_model_stats()  # 解包接收
print(f"准确率: {acc}, 损失: {loss}")

set —— 集合(无序不重复)

集合用 {}(但里面没有键值对)定义,自动去除重复元素,无固定顺序。适合去重、快速判断元素是否存在、以及集合运算(交集、并集)。

PYTHON · set 集合
# 创建集合(自动去重)
vocab = {"hello", "world", "hello", "python"}
print(vocab)  # {'hello', 'world', 'python'}(去重了)

# 用列表创建集合(去重技巧)
labels = ["cat", "dog", "cat", "bird", "dog"]
unique_labels = set(labels)     # {'cat', 'dog', 'bird'}
print(len(unique_labels))        # 3 个唯一类别

# 检查元素(速度比列表快很多)
print("cat" in unique_labels)    # True

# 集合运算(在 NLP 词汇分析中常用)
vocab_a = {"apple", "banana", "cat"}
vocab_b = {"cat", "dog", "banana"}

print(vocab_a & vocab_b)   # 交集:{'cat', 'banana'}(共有的)
print(vocab_a | vocab_b)   # 并集:{'apple', 'banana', 'cat', 'dog'}
print(vocab_a - vocab_b)   # 差集:{'apple'}(A 有 B 没有)

四种数据结构对比

类型符号有序可修改允许重复AI 中的典型用途
list [] 训练损失历史、消息列表、数据集
dict {k:v} ✅(3.7+) key唯一 API 参数、配置文件、模型输出
tuple () 图像尺寸、坐标、函数多返回值
set {} 词汇表、去重、类别标签集合

底层原理:为什么选这种结构?

了解数据结构的底层实现,能帮你在 AI 项目中做出更好的性能选择。

  • list 的内存布局 Python list 是动态数组,内存中连续存储元素引用(指针)。随机访问 list[i] 是 O(1),尾部追加 append() 平均 O(1),中间插入 insert(0, x) 是 O(n)(需要移动所有元素)。AI 中收集训练损失时应用 append() 而非 insert(0, x)
  • dict 的哈希表实现 dict 底层是哈希表,key 查找是 O(1) 平均时间复杂度。这就是为什么 "key" in dict"key" in list 快——list 需要线性搜索 O(n)。Python 3.7+ 起 dict 保证插入顺序(有序字典)。
  • set 的哈希特性 set 也是基于哈希表,成员检查 x in set 是 O(1),比 list 的 O(n) 快得多。NLP 中检查某词是否在词汇表时,应使用 set 而不是 list
  • tuple 的不可变优势 tuple 不可变,因此可以作为 dict 的 key(list 不行!),也可以被 set 包含。在 NumPy/PyTorch 中,tensor.shape 返回的就是 tuple,因为形状不应该被修改。
PYTHON · 性能对比示例
import time

# 成员检查:set vs list 的速度差距
vocab_list = list(range(100000))   # 10万词的词汇表(列表)
vocab_set  = set(range(100000))    # 10万词的词汇表(集合)

# 查询 99999 是否在词汇表(最坏情况)
target = 99999

t0 = time.time()
for _ in range(10000):
    target in vocab_list          # O(n) 线性搜索
print(f"list: {time.time()-t0:.3f}s")  # 约 3-4 秒

t0 = time.time()
for _ in range(10000):
    target in vocab_set           # O(1) 哈希查找
print(f"set:  {time.time()-t0:.3f}s")  # 约 0.001 秒,快 1000 倍!

# tuple 可以作为 dict 的 key
cache = {}
cache[(224, 224)] = "resnet50_features"  # ✅ tuple 可以作 key
# cache[[224, 224]] = ...   # ❌ list 不能作 key,会 TypeError

# 计数器(统计类别频率)
from collections import Counter
labels = ["cat", "dog", "cat", "bird", "cat", "dog"]
counts = Counter(labels)
print(counts)  # Counter({'cat': 3, 'dog': 2, 'bird': 1})
print(counts.most_common(2))  # [('cat', 3), ('dog', 2)]
⚠️

浅拷贝与深拷贝陷阱

列表赋值 b = a 只是复制了引用,ab 指向同一个列表!修改 b 会影响 a
浅拷贝:b = a.copy()b = a[:],创建新列表但元素本身仍共享。
深拷贝:import copy; b = copy.deepcopy(a),完全独立。
在 AI 中复制配置字典时,务必使用 config.copy()copy.deepcopy(config),否则修改副本会影响原始配置。

💡

本章小结

Python 四种核心数据结构:list(有序可变,动态数组)、dict(键值映射,哈希表)、tuple(有序不可变)、set(无序不重复,哈希表)。选择原则:需要快速成员检查用 set;需要键值映射用 dict;数据顺序重要且需要修改用 list;不需要修改的序列用 tuple。AI 开发中 dict 是使用最频繁的结构,几乎所有 API 接口、配置和返回结果都是字典或字典的列表。注意浅拷贝与深拷贝的区别。