Python collections 模块
目录
学习目标
- 掌握
namedtuple创建命名元组 - 学会使用
Counter统计频率 - 理解
defaultdict的默认工厂机制 - 掌握
deque双端队列和OrderedDict
1. namedtuple 命名元组
namedtuple 创建带有命名字段的元组子类,兼具元组的不可变性和类的可读性。
from collections import namedtuple
# 定义命名元组
Point = namedtuple('Point', ['x', 'y'])
# 创建实例
p = Point(10, 20)
print(p) # Point(x=10, y=20)
print(p.x, p.y) # 10 20
print(p[0], p[1]) # 10 20(也可用索引)
# 解包
x, y = p
print(f"x={x}, y={y}")
# 不可变性
# p.x = 100 # AttributeError1.1 实际应用
from collections import namedtuple
# 定义员工
Employee = namedtuple('Employee', ['name', 'department', 'salary'])
employees = [
Employee("Alice", "Engineering", 80000),
Employee("Bob", "Sales", 60000),
Employee("Charlie", "Engineering", 90000),
]
# 访问更清晰
for emp in employees:
print(f"{emp.name} 在 {emp.department} 部门,薪水 ${emp.salary}")
# 转换为字典
emp_dict = emp._asdict()
print(emp_dict)
# 替换字段(创建新实例)
new_emp = emp._replace(salary=95000)
print(new_emp)2. Counter 计数器
Counter 是字典的子类,用于计数可哈希对象。
from collections import Counter
# 统计字符频率
text = "abracadabra"
counter = Counter(text)
print(counter)
# Counter({'a': 5, 'b': 2, 'r': 2, 'c': 1, 'd': 1})
# 获取最常见的
print(counter.most_common(3)) # [('a', 5), ('b', 2), ('r', 2)]
# 访问不存在的元素返回 0
print(counter['z']) # 02.1 Counter 运算
from collections import Counter
a = Counter(['a', 'b', 'a', 'c'])
b = Counter(['a', 'b', 'b', 'd'])
print("a:", a) # Counter({'a': 2, 'b': 1, 'c': 1})
print("b:", b) # Counter({'b': 2, 'a': 1, 'd': 1})
# 加法(合并计数)
print("a + b:", a + b) # Counter({'a': 3, 'b': 3, 'c': 1, 'd': 1})
# 减法
print("a - b:", a - b) # Counter({'a': 1, 'c': 1})
# 交集(取最小值)
print("a & b:", a & b) # Counter({'a': 1, 'b': 1})
# 并集(取最大值)
print("a | b:", a | b) # Counter({'a': 2, 'b': 2, 'c': 1, 'd': 1})2.2 实际应用
from collections import Counter
import re
# 统计单词频率
text = """
Python is great. Python is easy.
Python is powerful and Python is popular.
"""
words = re.findall(r'\b\w+\b', text.lower())
word_count = Counter(words)
print("单词频率:")
for word, count in word_count.most_common(5):
print(f" {word}: {count}")
# 统计列表元素
colors = ['red', 'blue', 'green', 'red', 'blue', 'red']
color_count = Counter(colors)
print(f"\n颜色统计: {color_count}")3. defaultdict 默认字典
defaultdict 在访问不存在的键时,会自动创建默认值。
from collections import defaultdict
# 普通字典的问题
d = {}
# d['a'].append(1) # KeyError!
# 使用 defaultdict
d = defaultdict(list)
d['a'].append(1)
d['a'].append(2)
d['b'].append(3)
print(dict(d)) # {'a': [1, 2], 'b': [3]}
# 默认值为 int(计数)
count = defaultdict(int)
for char in 'abracadabra':
count[char] += 1
print(dict(count))
# 默认值为 set
d = defaultdict(set)
d['fruits'].add('apple')
d['fruits'].add('banana')
d['fruits'].add('apple') # 重复被忽略
print(dict(d)) # {'fruits': {'apple', 'banana'}}3.1 实际应用:分组
from collections import defaultdict
# 按首字母分组
words = ['apple', 'bat', 'bar', 'atom', 'book', 'candy']
by_first_letter = defaultdict(list)
for word in words:
by_first_letter[word[0]].append(word)
print(dict(by_first_letter))
# {'a': ['apple', 'atom'], 'b': ['bat', 'bar', 'book'], 'c': ['candy']}
# 按长度分组
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'], 5: ['candy']}4. deque 双端队列
deque(double-ended queue)支持从两端快速添加和删除元素。
from collections import deque
# 创建
d = deque([1, 2, 3, 4, 5])
print(d) # deque([1, 2, 3, 4, 5])
# 右端操作
d.append(6) # 右侧添加
d.pop() # 右侧删除
# 左端操作
d.appendleft(0) # 左侧添加
d.popleft() # 左侧删除
print(d) # deque([1, 2, 3, 4, 5])4.1 deque 进阶
from collections import deque
# 指定最大长度(超出时自动从另一端删除)
d = deque(maxlen=3)
d.append(1)
d.append(2)
d.append(3)
print(d) # deque([1, 2, 3])
d.append(4) # 超出长度,左侧 1 被删除
print(d) # deque([2, 3, 4])
# 旋转
d = deque([1, 2, 3, 4, 5])
d.rotate(2) # 向右旋转2位
print(d) # deque([4, 5, 1, 2, 3])
d.rotate(-1) # 向左旋转1位
print(d) # deque([5, 1, 2, 3, 4])
# 清空
d.clear()
print(d) # deque([])4.2 与列表的性能对比
from collections import deque
import time
# deque 左端添加更快
d = deque()
l = []
# deque 左端添加
start = time.time()
for i in range(100000):
d.appendleft(i)
print(f"deque appendleft: {time.time() - start:.4f}s")
# list 左端添加(很慢)
start = time.time()
for i in range(100000):
l.insert(0, i)
print(f"list insert(0): {time.time() - start:.4f}s")5. OrderedDict 有序字典
OrderedDict 保持键值对的插入顺序(Python 3.7+ 普通 dict 也保持顺序,但 OrderedDict 有额外功能)。
from collections import OrderedDict
# 创建有序字典
od = OrderedDict()
od['a'] = 1
od['b'] = 2
od['c'] = 3
print(od) # OrderedDict([('a', 1), ('b', 2), ('c', 3)])
# 移动到末尾
od.move_to_end('a')
print(od) # OrderedDict([('b', 2), ('c', 3), ('a', 1)])
# 移动到开头
od.move_to_end('c', last=False)
print(od) # OrderedDict([('c', 3), ('b', 2), ('a', 1)])
# 反转
print(reversed(od)) # 反转迭代器5.1 实现 LRU Cache
from collections import OrderedDict
class LRUCache:
"""简单 LRU 缓存实现"""
def __init__(self, capacity):
self.capacity = capacity
self.cache = OrderedDict()
def get(self, key):
if key not in self.cache:
return -1
# 移动到末尾(最近使用)
self.cache.move_to_end(key)
return self.cache[key]
def put(self, key, value):
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)
def __repr__(self):
return f"LRUCache({dict(self.cache)})"
# 使用
cache = LRUCache(2)
cache.put(1, "a")
cache.put(2, "b")
print(cache) # LRUCache({1: 'a', 2: 'b'})
cache.get(1) # 使用 1
cache.put(3, "c") # 2 被淘汰
print(cache) # LRUCache({1: 'a', 3: 'c'})6. ChainMap 链式映射
ChainMap 将多个字典组合在一起,查找时按顺序遍历。
from collections import ChainMap
# 组合多个字典
defaults = {'theme': 'light', 'language': 'en'}
user_prefs = {'theme': 'dark'}
# 用户设置优先于默认设置
settings = ChainMap(user_prefs, defaults)
print(settings['theme']) # dark(来自 user_prefs)
print(settings['language']) # en(来自 defaults)
print(dict(settings)) # {'theme': 'dark', 'language': 'en'}
# 添加新值到第一个映射
settings['font'] = 'Arial'
print(user_prefs) # {'theme': 'dark', 'font': 'Arial'}本节小结
- namedtuple:创建带名字的元组,提高代码可读性
- Counter:快速统计元素频率,支持数学运算
- defaultdict:自动提供默认值的字典,避免 KeyError
- deque:双端队列,两端操作 O(1),支持 maxlen
- OrderedDict:有序字典,支持 move_to_end
- ChainMap:链式组合多个字典,不复制数据
练习
- 使用
Counter统计一段文本中每个汉字出现的频率 - 使用
defaultdict实现一个多级字典(字典的字典) - 使用
deque实现一个滑动窗口平均值计算器 - 使用
namedtuple重构你项目中的简单数据类