Python 内置模块 collections 常用工具
今天我们来聊聊 Python 的 collections
模块,这个模块就像一个百宝箱,里面装了一堆超级实用的工具,能让你的代码更简洁、更高效。collections
提供了一些高级数据结构,比 Python 内置的列表、字典、集合等更强大,适合处理一些特定场景。
参考文章:Python 内置模块 collections | 简单一点学习 easyeasy.me
一、为什么要用 collections 模块?
Python 的内置数据类型(像 list
、dict
、set
)已经很好用了,但有时候会遇到一些特殊需求,比如需要一个字典能记住插入顺序,或者需要一个计数器来统计元素出现的次数。这时候,collections
模块就派上用场了!它提供了一些专门的数据结构,帮你更高效地解决问题。
这篇文章我们会重点介绍以下几个 collections
模块的常用工具:
namedtuple
:给元组取名字,代码更清晰deque
:双端队列,操作两端超快Counter
:计数神器,统计元素频率OrderedDict
:记住插入顺序的字典defaultdict
:自带默认值的字典
二、namedtuple:给元组取个名字
元组 (tuple
) 很好用,但它只能通过索引访问,比如 person[0]
、person[1]
,看着有点费劲。namedtuple
就像给元组的每个位置取了个名字,让你可以用 person.name
这样直观的方式访问。
用法
namedtuple
是一个工厂函数,用来创建带命名的元组类。基本语法是:
from collections import namedtuple
TypeName = namedtuple('TypeName', ['field1', 'field2', ...])
例子
假设我们要存一个人的信息,包括名字、年龄和城市:
from collections import namedtuple# 创建一个 Person 类
Person = namedtuple('Person', ['name', 'age', 'city'])# 创建一个 Person 实例
alice = Person(name='Alice', age=25, city='Beijing')# 访问属性
print(alice.name) # 输出: Alice
print(alice.age) # 输出: 25
print(alice.city) # 输出: Beijing# 也可以像元组一样用索引
print(alice[0]) # 输出: Alice
优点
- 代码可读性强,用
alice.name
比alice[0]
直观。 - 比字典占用内存少,因为它是元组的子类,数据不可变。
- 支持元组的所有操作,比如拆包、迭代等。
试试创建一个 Point
的 namedtuple
,表示 2D 坐标点,包含 x
和 y
,然后创建一个点 (3, 4)
,打印它的坐标。
Point = namedtuple('Point', ['x', 'y'])
p = Point(x=3, y=4)
print(f"Point: ({p.x}, {p.y})") # 输出: Point: (3, 4)
三、deque:双端队列,左右都快
deque
(读作 “deck”,双端队列)是一个高效的数据结构,适合在两端添加或删除元素。相比 list
,deque
在首尾操作上速度更快,尤其适合队列和栈的场景。
用法
deque
的基本操作包括:
append
/appendleft
:在右端/左端添加元素pop
/popleft
:在右端/左端删除并返回元素extend
/extendleft
:在右端/左端添加多个元素
例子
我们来模拟一个任务队列:
from collections import deque# 创建一个双端队列
tasks = deque(['task1', 'task2', 'task3'])# 右端添加任务
tasks.append('task4')
print(tasks) # 输出: deque(['task1', 'task2', 'task3', 'task4'])# 左端添加优先任务
tasks.appendleft('urgent_task')
print(tasks) # 输出: deque(['urgent_task', 'task1', 'task2', 'task3', 'task4'])# 处理任务(从左端取)
current_task = tasks.popleft()
print(f"处理任务: {current_task}") # 输出: 处理任务: urgent_task
print(tasks) # 输出: deque(['task1', 'task2', 'task3', 'task4'])
优点
- 首尾操作的时间复杂度是 O(1),而
list
的头部操作是 O(n)。 - 适合实现队列、栈,或者需要频繁在两端操作的场景。
用 deque
实现一个简单的栈(后进先出):
stack = deque()
stack.append('a')
stack.append('b')
print(stack.pop()) # 输出: b
print(stack.pop()) # 输出: a
四、Counter:计数神器
Counter
是一个专门用来计数的工具,特别适合统计列表、字符串等可迭代对象中元素的出现次数。它返回一个字典-like 的对象,键是元素,值是出现的次数。
用法
直接把可迭代对象丢给 Counter
,它会帮你统计好。
例子
统计一句话中每个单词出现的次数:
from collections import Countersentence = "apple banana apple orange banana apple"
words = sentence.split()
word_counts = Counter(words)print(word_counts) # 输出: Counter({'apple': 3, 'banana': 2, 'orange': 1})
print(word_counts['apple']) # 输出: 3# 获取出现次数最多的两个单词
print(word_counts.most_common(2)) # 输出: [('apple', 3), ('banana', 2)]
更多用法
- 合并计数:可以用
+
或-
合并两个Counter
。 - 获取所有元素:
elements()
方法返回一个迭代器,按计数展开元素。
c1 = Counter(a=3, b=1)
c2 = Counter(a=1, b=2)
print(c1 + c2) # 输出: Counter({'a': 4, 'b': 3})
print(list(c1.elements())) # 输出: ['a', 'a', 'a', 'b']
统计一个字符串中每个字符的出现次数:
text = "hello"
char_counts = Counter(text)
print(char_counts) # 输出: Counter({'l': 2, 'h': 1, 'e': 1, 'o': 1})
五、OrderedDict:记住插入顺序的字典
在 Python 3.7 之前,普通字典不保证键的插入顺序。OrderedDict
是一个特殊的字典,能记住键值对的插入顺序。虽然 Python 3.7+ 的普通字典已经支持顺序,但 OrderedDict
还有一些独特的功能,比如移动元素。
用法
跟普通字典差不多,但它会记住插入顺序。
例子
记录学生的加入顺序:
from collections import OrderedDictstudents = OrderedDict()
students['Alice'] = 95
students['Bob'] = 88
students['Charlie'] = 92print(students) # 输出: OrderedDict([('Alice', 95), ('Bob', 88), ('Charlie', 92)])# 移动某个键到末尾
students.move_to_end('Bob')
print(students) # 输出: OrderedDict([('Alice', 95), ('Charlie', 92), ('Bob', 88)])
优点
- 明确保证插入顺序,适合需要顺序的场景。
- 有
move_to_end
和popitem(last=False)
等方法,灵活操作顺序。
创建一个 OrderedDict
,按顺序添加三个键值对,然后把第一个键移动到末尾:
od = OrderedDict(a=1, b=2, c=3)
od.move_to_end('a')
print(od) # 输出: OrderedDict([('b', 2), ('c', 3), ('a', 1)])
六、defaultdict:自带默认值的字典
普通字典在访问不存在的键时会报 KeyError
,而 defaultdict
会在访问不存在的键时自动创建一个默认值,省去手动初始化的麻烦。
用法
创建 defaultdict
时需要指定一个默认工厂函数,比如 int
、list
、set
等。
例子
统计每个字母开头的单词:
from collections import defaultdictwords = ['apple', 'banana', 'ant', 'bear', 'cat']
d = defaultdict(list) # 默认值是空列表for word in words:d[word[0]].append(word)print(d) # 输出: defaultdict(<class 'list'>, {'a': ['apple', 'ant'], 'b': ['banana', 'bear'], 'c': ['cat']})
更多例子
用 int
作为默认值来计数:
counts = defaultdict(int)
for char in "hello":counts[char] += 1print(counts) # 输出: defaultdict(<class 'int'>, {'h': 1, 'e': 1, 'l': 2, 'o': 1})
优点
- 避免
KeyError
,简化代码。 - 适合需要动态初始化的场景,比如分组、计数。
用 defaultdict
统计一个列表中每个数字的出现次数:
numbers = [1, 2, 2, 3, 1, 4]
counts = defaultdict(int)
for num in numbers:counts[num] += 1
print(counts) # 输出: defaultdict(<class 'int'>, {1: 2, 2: 2, 3: 1, 4: 1})
七、总结和实践建议
collections
模块的这些工具各有妙用,总结一下:
namedtuple
:适合定义轻量级、不可变的数据结构。deque
:适合需要高效首尾操作的场景,比如队列和栈。Counter
:统计元素频率的神器。OrderedDict
:需要记住插入顺序时用(Python 3.7+ 可选)。defaultdict
:避免KeyError
,简化动态初始化。