第 31 课 · 进阶 · 19 分钟

Python collections

选择正确的容器,而不是每次手写同样的逻辑。

collectionsCounterdefaultdictdeque

Maya 想知道最好卖的饮料、正在排队的订单,还有按杯型统计的数量。三个循环也能做,三个容器做得更清楚。哪个容器不用 dict.get 就能计数?

collections 模块为常见任务提供专用容器:计数、按键分组,以及在两端添加或删除元素。它们各自只解决一个小问题,却能让代码意图更清楚。 你还会保留固定长度的窗口、叠加配置,并比较库存。

用 Counter 计数

Counter 是字典的子类,用来统计出现次数。访问不存在的键会返回零,而不是抛出 KeyError。most_common 会把次数最高的项排在前面,适合做汇总和排行。

from collections import Counter

votes = ["tea", "coffee", "tea"]
counts = Counter(votes)
print(counts["tea"])
print(counts.most_common(1))

先计数,再排序

用 defaultdict 分组

访问不存在的键时,defaultdict 会调用工厂函数。把 list 作为工厂,每个新键都会从空列表开始。这样就不必在追加前反复检查键是否存在。

from collections import defaultdict

groups = defaultdict(list)
for name, team in [("Ada", "A"), ("Grace", "B"), ("Lin", "A")]:
    groups[team].append(name)

print(dict(groups))

每个新键都有一个列表

用 deque 操作两端

deque 支持在两端高效添加和删除。popleft 从队首移除最早的元素,append 从队尾加入。它很适合队列和滑动窗口。

from collections import deque

queue = deque(["a", "b"])
queue.append("c")
queue.popleft()
print(list(queue))

双端队列

保留固定长度的滚动窗口

带 maxlen 的 deque 会自动丢掉最旧的元素,很适合移动平均或短活动流:不用切片复制,也不用维护索引。窗口大小在创建 deque 时就固定下来。

from collections import deque

window = deque(maxlen=3)
for reading in [10, 12, 11, 15]:
    window.append(reading)
    print(list(window), round(sum(window) / len(window), 1))

新值进来,旧值自动离开

用 ChainMap 叠加配置

ChainMap 从左到右查找各个映射。把用户设置放前面、默认值放后面,用户没有设置的键会自动落到默认层,不需要复制合并整本字典。new_child() 可以为一个请求或一个页面创建临时覆盖层。

from collections import ChainMap

defaults = {"theme": "light", "page_size": 20}
user = {"theme": "dark"}
settings = ChainMap(user, defaults)
print(settings["theme"], settings["page_size"])
print("page_size" in user)
scoped = settings.new_child({"page_size": 50})
print(scoped["page_size"], settings["page_size"])

不复制字典也能覆盖

Counter 运算只保留正库存

两个 Counter 相减时,零和负数会被丢掉。用来算剩余库存很方便,但结果不再是完整账本。交集 & 对每个键保留较小的计数,回答的是另一个问题:两份库存有多少共同部分?

from collections import Counter

stock = Counter({"tea": 3, "latte": 2})
sold = Counter({"tea": 2, "latte": 2, "mocha": 1})
left = stock - sold
print(left)
print(list(left.elements()))
print(stock & sold)

剩余库存和共同库存不是一回事

本课要点

  • Counter 统计可哈希值,缺失键返回零。
  • most_common 按次数从高到低排序。
  • defaultdict 会为缺失键创建默认值。
  • deque 支持高效的两端操作。
  • 离开局部代码时,可以把专用容器转换成普通列表或字典。
  • 滚动窗口用 maxlen,配置优先级用 ChainMap,库存视图用 Counter 运算。

常见错误

  • 键可能不存在时,仍然对普通字典使用 counts[key]。
  • 忘记 defaultdict 在读取时也会插入键。
  • 需要队列时反复使用 list.pop(0),而没有考虑 deque。
  • 把 Counter 相减当成完整审计记录:零和负数结果会消失。
轮到你

用 Counter 统计 ['a', 'b', 'a'],再往 defaultdict(list) 里添加一个新团队。

动手试试

改改变量、先预测输出,再运行代码。第一次点击“运行代码”时会下载 Python 运行时,可能需要几秒钟。

浏览器内 Python本地运行 · 无需账号
首次运行会从 CDN 下载 Pyodide。修改后点击“运行代码”。

练习本课

12 道题,作答后立即看到解释。

查看 12 道题答案开始练习
← itertoolsfunctools →