首页 / Python3 入门教程 / collections 与数据容器

Python3 入门教程

collections 与数据容器

本教程共 70 篇 · 第 50 篇 · 更新于 2026-07-22 · 约 5 分钟阅读

PythonPython3 入门教程collectionsdequeCounternamedtuple

50. collections 与数据容器

本节目标:掌握 collections 模块中的高级数据结构,在合适场景下替代基础容器,写出更高效的代码。

列表、字典、元组、集合是 Python 的基础容器,日常开发够用了。但在某些场景下,它们不够方便或不够高效。collections 模块提供了一系列扩展数据结构,填补这些空白。

namedtuple:带名字的元组

普通元组通过索引访问,代码可读性差:

point = (3, 4)
print(point[0])  # 3 —— 0 是什么意思?

namedtuple 给每个位置起个名字:

from collections import namedtuple

Point = namedtuple('Point', ['x', 'y'])
p = Point(3, 4)

print(p.x)       # 3
print(p.y)       # 4
print(p[0])      # 也支持索引访问

namedtuple 本质还是元组,不可变,内存占用和普通元组一样小。它比定义一个完整的类简洁得多:

# 不需要这么写
class Point:
    def __init__(self, x, y):
        self.x = x
        self.y = y

# namedtuple 一行搞定
Point = namedtuple('Point', 'x y')
Tip

namedtuple 适合表示「轻量级的结构化数据」:坐标点、颜色 RGB、数据库记录等。如果数据需要方法或可变,还是用普通类。

Python 3.7+ 还提供了 typing.NamedTuple,支持类型注解:

from typing import NamedTuple

class Point(NamedTuple):
    x: float
    y: float

p = Point(3.0, 4.0)

deque:双端队列

列表的 pop(0)insert(0, x) 操作很慢,因为需要移动所有后续元素。deque(double-ended queue)在两端操作都是 O(1):

from collections import deque

d = deque([1, 2, 3])
d.append(4)        # 右侧添加
d.appendleft(0)    # 左侧添加
print(d)           # deque([0, 1, 2, 3, 4])

d.pop()            # 右侧弹出
d.popleft()        # 左侧弹出
Note

deque 内部用双向链表实现,两端操作极快。但如果你需要频繁访问中间元素,deque 不如列表快。按需选择。

限制长度做缓存

d = deque(maxlen=3)
d.append(1)
d.append(2)
d.append(3)
d.append(4)
print(d)  # deque([2, 3, 4]) —— 1 被自动挤出

固定长度的 deque 非常适合做「滑动窗口」和「最近 N 条记录」缓存。

Counter:自动计数器

统计元素出现次数,不用手写字典:

from collections import Counter

text = "abracadabra"
counts = Counter(text)
print(counts)  # Counter({'a': 5, 'b': 2, 'r': 2, 'c': 1, 'd': 1})

print(counts.most_common(2))  # [('a', 5), ('b', 2)]

Counter 支持数学运算:

c1 = Counter(a=3, b=1)
c2 = Counter(a=1, b=2)
print(c1 + c2)  # Counter({'a': 4, 'b': 3})
print(c1 - c2)  # Counter({'a': 2})

defaultdict:自动初始化字典

普通字典访问不存在的键会抛 KeyErrordefaultdict 会在键不存在时自动创建默认值:

from collections import defaultdict

# 值类型是 list
d = defaultdict(list)
d['fruits'].append('apple')
d['fruits'].append('banana')
print(d)  # defaultdict(<class 'list'>, {'fruits': ['apple', 'banana']})

# 值类型是 int(适合做计数)
counts = defaultdict(int)
for word in ['apple', 'banana', 'apple']:
    counts[word] += 1
print(counts)  # defaultdict(<class 'int'>, {'apple': 2, 'banana': 1})
Warning

defaultdictdefault_factory 必须是无参可调用对象。如果你写 defaultdict(list())(调用了 list),所有键会共享同一个列表对象。正确写法是 defaultdict(list)(传入 list 类型本身)。

OrderedDict:保持插入顺序的字典

Python 3.7+ 的普通字典已经保持插入顺序了,OrderedDict 的存在感变弱。但它有两个独特功能:

  1. 明确表达「顺序重要」的意图:读代码的人一眼就知道这个字典的顺序被依赖。
  2. 支持重新排序的方法
from collections import OrderedDict

od = OrderedDict([('a', 1), ('b', 2), ('c', 3)])
od.move_to_end('a')       # 把 'a' 移到最后
od.popitem(last=False)    # 弹出第一个元素
Note

新项目如果只需要「保持插入顺序」,直接用普通 dict 就行。OrderedDict 只在需要 move_to_end()popitem(last=False) 时才派上用场。

ChainMap:多层字典的联合视图

把多个字典「虚拟合并」成一个,不复制数据:

from collections import ChainMap

defaults = {'theme': 'light', 'lang': 'en'}
user_prefs = {'theme': 'dark'}

cm = ChainMap(user_prefs, defaults)
print(cm['theme'])  # dark(user_prefs 优先)
print(cm['lang'])   # en(从 defaults 找到)

修改 ChainMap 只会影响第一个字典:

cm['font'] = '16px'
print(user_prefs)  # {'theme': 'dark', 'font': '16px'}
Tip

ChainMap 适合处理「默认值 + 用户配置」的场景,比如命令行参数解析、应用配置合并。比手动 copyupdate 更省内存。

小结

数据结构解决的问题对应基础容器
namedtuple元组没有字段名tuple
deque列表两端操作慢list
Counter手动计数字典啰嗦dict
defaultdict键不存在时抛异常dict
OrderedDict需要显式顺序控制dict
ChainMap多层配置合并dict

collections 模块里的工具不是「必用」,而是「在合适的时候用」。基础容器能解决 80% 的问题,遇到性能瓶颈或代码重复时,再考虑升级。


来源:参考了 liaoxuefeng「collections」、pythondoc「collections 模块」等,改写后所得。