深入理解 Python 的 collections 模块:从基础到高级应用
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 内置容器的"专业升级版":
- namedtuple:用可读属性替代魔法数字索引
- deque:双端操作的高性能队列
- Counter:声明式计数与集合运算
- defaultdict:消除 KeyError 的优雅方案
- OrderedDict:可控顺序的字典(特别是
move_to_end) - ChainMap:零内存开销的配置合并
掌握这些工具,能让你写出更 Pythonic、更高效的代码。它们都是标准库的一部分,无需安装即可使用,是每位 Python 开发者工具箱中的必备利器。
感兴趣的小伙伴给博主点个关注吧,后续还会更新~
更多推荐



所有评论(0)