Python 的 collections 模块是标准库中一颗隐藏的明珠,它提供了一系列专用容器数据类型,扩展了内置容器(list、dict、set、tuple)的功能。这些数据结构针对特定场景优化,能让你的代码更简洁、高效且富有表达力。本文将深入剖析 collections 模块的核心组件,从基础用法到高级实战技巧。


1. namedtuple:具名元组

核心概念

namedtuple 创建具有命名字段的元组子类,解决了普通元组通过索引访问可读性差的问题。它兼具元组的不可变性和类的可读性,内存占用却与普通元组相同。

基础用法

from collections import namedtuple

# 定义 Point 类型
Point = namedtuple('Point', ['x', 'y'])
p = Point(3, 4)

# 支持多种访问方式
print(p.x, p.y)      # 通过属性访问:3 4
print(p[0], p[1])    # 通过索引访问:3 4
print(p._asdict())   # 转为 OrderedDict:{'x': 3, 'y': 4}

高级特性

# 从字典创建
data = {'x': 10, 'y': 20}
p = Point(**data)

# 替换字段(返回新实例,保持不可变性)
p2 = p._replace(x=100)

# 默认值(Python 3.7+)
Person = namedtuple('Person', ['name', 'age', 'gender'], defaults=['Unknown', 0])
p = Person('Alice')  # Person(name='Alice', age='Unknown', gender=0)

实战场景

  • 数据库记录:替代轻量级数据类,存储查询结果
  • CSV/JSON 解析:为结构化数据提供语义化访问
  • 函数多返回值:比返回普通元组更清晰

2. deque:双端队列

核心概念

deque(double-ended queue)是线程安全的双端队列,在两端添加或弹出元素的时间复杂度为 O(1),而列表(list)在头部插入/删除是 O(n)

基础操作

from collections import deque

dq = deque([1, 2, 3])

# 两端操作
dq.append(4)        # 右端添加:deque([1, 2, 3, 4])
dq.appendleft(0)    # 左端添加:deque([0, 1, 2, 3, 4])
dq.pop()            # 右端弹出:返回 4
dq.popleft()        # 左端弹出:返回 0

# 旋转
dq.rotate(1)        # 向右旋转1位
dq.rotate(-2)       # 向左旋转2位

性能对比

操作 list deque
末尾添加 O(1) O(1)
头部添加 O(n) O(1)
中间插入 O(n) O(n)
随机访问 O(1) O(n)

实战场景

# 1. 实现滑动窗口最大值(LeetCode 239)
def max_sliding_window(nums, k):
    dq = deque()
    result = []
    for i, num in enumerate(nums):
        # 移除窗口外元素
        while dq and dq[0] < i - k + 1:
            dq.popleft()
        # 移除较小元素
        while dq and nums[dq[-1]] < num:
            dq.pop()
        dq.append(i)
        if i >= k - 1:
            result.append(nums[dq[0]])
    return result

# 2. 作为栈使用(后进先出)
stack = deque()
stack.append('task1')
stack.append('task2')
task = stack.pop()  # task2

# 3. 作为队列使用(先进先出)
queue = deque()
queue.append('client1')
queue.append('client2')
client = queue.popleft()  # client1

3. Counter:计数器

核心概念

Counter 是字典的子类,专为计数设计。它将元素映射到出现次数,支持丰富的数学运算。

基础用法

from collections import Counter

# 从可迭代对象创建
colors = ['red', 'blue', 'red', 'green', 'blue', 'blue']
cnt = Counter(colors)
print(cnt)  # Counter({'blue': 3, 'red': 2, 'green': 1})

# 从字符串创建
char_count = Counter("mississippi")
print(char_count.most_common(2))  # [('i', 4), ('s', 4)]

# 访问不存在的元素返回 0,不会 KeyError
print(cnt['yellow'])  # 0

高级操作

# 数学运算
c1 = Counter(a=3, b=1)
c2 = Counter(a=1, b=2)

c1 + c2  # Counter({'a': 4, 'b': 3})  相加
c1 - c2  # Counter({'a': 2})          相减(只保留正数)
c1 & c2  # Counter({'a': 1, 'b': 1})  取最小值
c1 | c2  # Counter({'a': 3, 'b': 2})  取最大值

# 元素展开
list(cnt.elements())  # ['red', 'red', 'blue', 'blue', 'blue', 'green']

# 更新计数
cnt.update(['red', 'red'])  # 增量更新
cnt.subtract({'red': 2})    # 减量更新

实战场景:文本分析

from collections import Counter
import re

def analyze_text(text):
    # 词频统计
    words = re.findall(r'\b\w+\b', text.lower())
    word_freq = Counter(words)
    
    # 查找高频词
    stopwords = {'the', 'a', 'is', 'in', 'and'}
    filtered = word_freq - Counter(stopwords)  # 去除停用词
    return filtered.most_common(5)

text = "The quick brown fox jumps over the lazy dog. The fox is quick."
print(analyze_text(text))
# [('quick', 2), ('fox', 2), ('brown', 1), ('jumps', 1), ('over', 1)]

4. defaultdict:默认字典

核心概念

defaultdict 在访问不存在的键时,自动使用工厂函数创建默认值,彻底告别 KeyError 和繁琐的 if key in dict 检查。

基础用法

from collections import defaultdict

# 默认值为 int(0)
dd = defaultdict(int)
dd['views'] += 1  # 自动初始化为 0 再自增,无需检查键是否存在

# 默认值为 list
groups = defaultdict(list)
groups['fruits'].append('apple')
groups['fruits'].append('banana')  # 无需初始化列表

# 默认值为 set
tags = defaultdict(set)
tags['python'].add('programming')
tags['python'].add('language')

工厂函数技巧

# 嵌套 defaultdict
nested = defaultdict(lambda: defaultdict(int))
nested['level1']['level2'] += 1

# 自定义工厂
def default_user():
    return {'name': 'Anonymous', 'score': 0}

users = defaultdict(default_user)
print(users['new_user'])  # {'name': 'Anonymous', 'score': 0}

实战场景:数据分组

from collections import defaultdict

# 按长度分组单词
words = ['apple', 'bat', 'bar', 'atom', 'book', 'c']
by_length = defaultdict(list)

for word in words:
    by_length[len(word)].append(word)

print(dict(by_length))
# {5: ['apple'], 3: ['bat', 'bar', 'book'], 4: ['atom'], 1: ['c']}

5. OrderedDict:有序字典

核心概念

OrderedDict 能记住键值对的插入顺序。虽然 Python 3.7+ 的普通字典也保持插入顺序,但 OrderedDict 提供了额外的排序控制能力。

关键方法

from collections import OrderedDict

od = OrderedDict()
od['first'] = 1
od['second'] = 2
od['third'] = 3

# move_to_end:移动键到末尾或开头
od.move_to_end('first')           # 移到末尾
od.move_to_end('second', last=False)  # 移到开头

# popitem:按顺序移除
last = od.popitem()       # 移除最后插入的
first = od.popitem(last=False)  # 移除最先插入的

# 反转
reversed_od = OrderedDict(reversed(list(od.items())))

实战场景:LRU 缓存实现

from collections import OrderedDict

class LRUCache:
    def __init__(self, capacity: int):
        self.cache = OrderedDict()
        self.capacity = capacity
    
    def get(self, key: int) -> int:
        if key not in self.cache:
            return -1
        # 移动到末尾表示最近使用
        self.cache.move_to_end(key)
        return self.cache[key]
    
    def put(self, key: int, value: int) -> None:
        if key in self.cache:
            self.cache.move_to_end(key)
        self.cache[key] = value
        if len(self.cache) > self.capacity:
            # 移除最久未使用的(最前面的)
            self.cache.popitem(last=False)

6. ChainMap:链式映射

核心概念

ChainMap 将多个字典组合成单一视图,查找时按顺序遍历底层映射,不创建新字典,内存高效。

基础用法

from collections import ChainMap

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

# 创建链式映射(优先级从左到右)
config = ChainMap(cli_args, user_prefs, defaults)

print(config['theme'])  # 'dark'(来自 user_prefs)
print(config['lang'])   # 'en'(来自 defaults)
print(config['debug'])  # True(来自 cli_args)

# 修改只影响第一个映射
config['theme'] = 'blue'  # 修改 user_prefs

高级操作

# 添加新映射到链前
new_config = config.new_child({'font_size': 14})

# 获取父级视图(去掉第一个)
parent_view = config.parents

# 查看所有底层映射
print(config.maps)  # [{'debug': True}, {'theme': 'dark'}, {'theme': 'light', 'lang': 'en'}]

实战场景:配置层级

from collections import ChainMap
import os

# 多层配置:命令行 > 环境变量 > 配置文件 > 默认值
defaults = {'db_host': 'localhost', 'db_port': 3306, 'debug': False}
file_config = {'db_host': 'prod.db.com', 'db_port': 3306}
env_config = {k.replace('APP_', '').lower(): v 
              for k, v in os.environ.items() if k.startswith('APP_')}

config = ChainMap({}, env_config, file_config, defaults)

# 查找逻辑:先在命令行覆盖层找,再环境变量,再配置文件,最后默认值

7. UserDict、UserList、UserString:自定义容器基类

这些类封装了底层数据,便于创建自定义容器,比直接继承内置类型更安全(避免意外覆盖内置方法)。

from collections import UserDict

class CaseInsensitiveDict(UserDict):
    """大小写不敏感的字典"""
    def __setitem__(self, key, value):
        super().__setitem__(key.lower(), value)
    
    def __getitem__(self, key):
        return super().__getitem__(key.lower())
    
    def __contains__(self, key):
        return super().__contains__(key.lower())

d = CaseInsensitiveDict()
d['Key'] = 'value'
print(d['key'])  # 'value'
print(d['KEY'])  # 'value'

性能对比与选型指南

数据结构 适用场景 时间复杂度优势 内存特点
namedtuple 轻量不可变记录 访问 O(1) 与普通 tuple 相同
deque 队列/栈/滑动窗口 两端操作 O(1) 双向链表结构
Counter 频率统计 计数 O(n) 基于 dict
defaultdict 自动初始化分组 访问 O(1) 基于 dict
OrderedDict 需要重排序操作 move_to_end O(1) 维护双向链表
ChainMap 配置层级/上下文 查找 O(m*n) 零拷贝视图

总结

collections 模块提供了 Python 内置容器的"专业升级版":

  1. namedtuple:用可读属性替代魔法数字索引
  2. deque:双端操作的高性能队列
  3. Counter:声明式计数与集合运算
  4. defaultdict:消除 KeyError 的优雅方案
  5. OrderedDict:可控顺序的字典(特别是 move_to_end
  6. ChainMap:零内存开销的配置合并

掌握这些工具,能让你写出更 Pythonic、更高效的代码。它们都是标准库的一部分,无需安装即可使用,是每位 Python 开发者工具箱中的必备利器。
感兴趣的小伙伴给博主点个关注吧,后续还会更新~

Logo

这里是“一人公司”的成长家园。我们提供从产品曝光、技术变现到法律财税的全栈内容,并连接云服务、办公空间等稀缺资源,助你专注创造,无忧运营。

更多推荐