LLM系列:1.python入门:18.collections模块(高级容器数据类型)
collections模块(高级容器数据类型)
一. 认识 collections
==collections 是 Python 的内置标准库,无需安装即可直接导入。==它提供了一些额外的高级数据结构(本质上都是基于原生类型的封装与继承),用来替代 Python原生的 dict、list、set 和 tuple,从而让数据处理变得更加高效和优雅。
二. collections中字典相关类型(dict)
1. Counter类型 (字典的子类)
==说明:Counter 是 Python 原生字典 dict 的子类(继承自 dict)。==作为一种专门为统计设计的类,它具备普通字典的所有基本属性和方法,同时对其进行了高度的定制化扩展,使其能够更方便地进行频次统计操作。
在 NLP 的词表构建、词频统计中常用Counter类
(1).Counter() - 超级计数器
作用:作为 dict 的子类实例化方法,它专门用于统计可迭代对象中元素的出现次数。它的键(Key)是被统计的元素,值(Value)是该元素出现的次数。
from collections import Counter
Counter(iterable_or_mapping)
参数:
- 可迭代对象 (iterable_or_mapping): 需要被统计的列表、元组、字符串,或者是已经包含部分计数的字典。
返回值:
- 成功: 返回一个
Counter类型的实例对象(表现与字典高度一致),内部自动按元素出现的频次进行了整理。
示例:
from collections import Counter
# 1. 传入列表进行自动计数
words_list = ['apple', 'banana', 'apple', 'orange', 'banana', 'apple']
word_counts = Counter(words_list)
print(word_counts)
# 结果: Counter({'apple': 3, 'banana': 2, 'orange': 1})
# 2. 像字典一样获取某个元素的次数
print(word_counts['apple']) # 结果: 3
# 3. 遇到不存在的元素不会报错,而是返回 0!(这是与普通 dict 最大的区别之一)
print(word_counts['grape']) # 结果: 0
(2).most_common() - 提取高频元素
作用:这是 Counter 类最强大的专属实例方法!它会根据元素的出现次数从高到低排序,并返回一个包含元组的列表。这是 NLP 截断低频词、构建词表的最核心方法!
Counter.most_common(n=None)
参数:
- 数量 (n): 可选参数。指定返回出现频次排名前
n的元素。如果不填,则返回所有元素排序后的结果 (int)。
返回值:
- 成功: 返回一个由
(元素, 频次)元组组成的列表 (list[tuple])。
示例:
from collections import Counter
words_list = ['a', 'b', 'b', 'c', 'c', 'c', 'd', 'd', 'd', 'd']
counts = Counter(words_list)
# 获取频次最高的前 2 个词
top_2 = counts.most_common(2)
print(top_2)
# 结果: [('d', 4), ('c', 3)]
(3).elements() - 展开统计元素
作用:返回一个迭代器,其中每个元素将重复出现其计数值所代表的次数。常用于将统计好的词频字典“还原”回原始的数据流。
Counter.elements()
返回值:
- 成功: 返回一个
itertools.chain类型的迭代器对象。注意:频次小于 1 的元素会被忽略。
示例:
c = Counter(a=4, b=2, c=0, d=-2)
print(list(c.elements()))
# 结果: ['a', 'a', 'a', 'a', 'b', 'b'] (c和d被忽略了)
(4).update() 与 subtract() - 批量增减计数
作用:普通字典的 update 是直接覆盖同名 Key 的 Value,而 Counter 重写了 update,它的逻辑是累加!同理,subtract 则是批量相减。
Counter.update(iterable_or_mapping)
Counter.subtract(iterable_or_mapping)
参数:
- 可迭代对象 (iterable_or_mapping): 需要新增或扣除计数的数据源。
示例:
c = Counter({'apple': 3, 'banana': 2})
# 1. 累加频次 (update)
c.update(['apple', 'apple', 'orange'])
print(c)
# 结果: Counter({'apple': 5, 'banana': 2, 'orange': 1})
# 2. 扣减频次 (subtract)
c.subtract({'apple': 2, 'banana': 5})
print(c)
# 结果: Counter({'apple': 3, 'orange': 1, 'banana': -3}) # 允许出现负数!
2. defaultdict类型 (字典的子类)
==说明:defaultdict 是 Python 原生字典 dict 的子类dict)。==它与原生字典最大的区别在于,当试图访问一个不存在的键时,它不会抛出 KeyError 异常,而是会自动调用一个预先设定的工厂函数,为这个缺失的键生成一个默认值。
(1).defaultdict() - 默认值字典
作用:创建自带默认值机制的字典。这极大地简化了数据分组、多值映射以及统计的代码逻辑,让你无需再手动编写 if key not in d: 或使用 dict.setdefault()。
from collections import defaultdict
defaultdict(default_factory=None, /[, ...])
参数:
-
默认值工厂函数 (default_factory): 必须是一个可调用对象(如
int,list,set,或者自定义无参函数)。当访问缺失的键时,系统会无参调用它来生成默认值。如果传入None,它将退化为普通字典的行为(找不到键时报错)。defaultdict(int)—— 计数器之王
- 无参调用:
int()默认返回0。 - 场景: 统计频次。遇到新元素,默认初始化为 0,然后直接
+= 1。
defaultdict(list)—— 分组聚合神器
- 无参调用:
list()默认返回[]。 - 场景: 一对多映射。比如把同一班级的学生名字归拢到一个列表里,遇到新班级直接
.append(名字),绝对不会报错。
defaultdict(set)—— 去重分组神器
- 无参调用:
set()默认返回空集合set()。 - 场景: 和
list类似,但自带去重功能。如果你想把同一班级的学生归拢起来,又怕名字重复录入,用它然后直接调用.add(名字)。
defaultdict(lambda: "未知数据")—— 自定义花式默认值
- 无参调用:
lambda: "未知数据"执行后返回字符串"未知数据"。 - 场景: 有时候你不想要 0 也不想要空列表,你就想要一个特定的默认字符串或数字。既然必须传函数,那就当场写一个无需参数的匿名函数(lambda)传进去!
返回值:
- 成功: 返回一个
defaultdict实例。查询缺失键时,自动赋默认值并返回该默认值。
示例:
from collections import defaultdict
# 1. 传入 list 作为工厂函数,实现一对多映射 (非常适合做分类/分组聚合)
data = [('fruit', 'apple'), ('fruit', 'banana'), ('vegetable', 'carrot')]
group_dict = defaultdict(list)
for category, item in data:
# 遇到新的 category,会自动创建空列表 [],并直接进行 append 操作
group_dict[category].append(item)
print(dict(group_dict))
# 结果: {'fruit': ['apple', 'banana'], 'vegetable': ['carrot']}
# 2. 传入 int 作为工厂函数 (默认值为 0),用于计数
count_dict = defaultdict(int)
count_dict['apple'] += 1 # 即使 'apple' 之前不存在,也会默认初始化为 0,再 +1
print(count_dict['apple'])
# 结果: 1
(2).default_factory属性 - 动态修改工厂
作用:defaultdict 将工厂函数保存在实例的 default_factory 属性中。面试技巧:可以在运行时动态修改该属性,随时切换默认值生成的逻辑!
示例:
d = defaultdict(int) # 默认返回 0
print(d['a']) # 结果: 0
# 动态修改默认值工厂
d.default_factory = list # 改为默认返回空列表 []
print(d['b']) # 结果: []
3. OrderedDict类型 (字典的子类)
==说明:OrderedDict 也是 Python 原生字典 dict 的子类(继承自 dict)。==作为一种特殊的数据类型,它在拥有原生字典全部特性的基础上,底层额外维护了一个双向链表,以此来严格记录键值对的插入顺序,并提供相关的排序和调整操作。
(1).OrderedDict() - 有序字典
作用:OrderedDict 类的实例化方法,顾名思义是创建一个“记录了元素插入顺序”的字典对象。虽然自 Python 3.7 开始,原生的 dict 也在底层实现了顺序记录,但 OrderedDict 依然具有独特价值。它不仅在代码层面上明确表达了“顺序极其重要”的语义,而且提供了原生字典所不具备的专属排序方法。
from collections import OrderedDict
OrderedDict([items])
参数:
- 初始化数据 (items): 可选参数。可以是包含键值对的元组列表,也可以是另一个字典。
返回值:
- 成功: 返回一个
OrderedDict类型的实例对象,该对象严格保证元素插入顺序。
示例:
from collections import OrderedDict
od = OrderedDict()
od['apple'] = 3
od['banana'] = 2
od['orange'] = 1
print(od)
# 结果: OrderedDict([('apple', 3), ('banana', 2), ('orange', 1)])
(2).move_to_end() - 调整元素位置
作用:将一个已经存在的键移动到字典的最前端或最后端。常用于实现 LRU (最近最少使用) 缓存淘汰算法!
OrderedDict.move_to_end(key, last=True)
参数:
- 键 (key): 需要被移动的目标键。
- 位置 (last): 默认
True,移动到末尾;设为False,移动到开头。
示例:
od = OrderedDict.fromkeys('abcde')
print(od) # OrderedDict([('a', None), ('b', None), ('c', None), ('d', None), ('e', None)])
od.move_to_end('b') # 移到最后
od.move_to_end('d', False) # 移到最前
print(list(od.keys()))
# 结果: ['d', 'a', 'c', 'e', 'b']
(3).popitem() - 有序弹出
作用:弹出并返回字典中的一个 (key, value) 键值对。由于是有序字典,你可以精确控制是弹出 LIFO(后进先出)还是 FIFO(先进先出)。
OrderedDict.popitem(last=True)
参数:
- 位置 (last): 默认
True(类似栈,弹出最后插入的元素);设为False(类似队列,弹出最先插入的元素)。
示例:
od = OrderedDict([('a', 1), ('b', 2), ('c', 3)])
print(od.popitem(last=False)) # 弹出最旧的
# 结果: ('a', 1)
4. UserDict类型 (字典的包装类)
说明:UserDict 是一个专门为了方便开发者“自定义/重写字典功能”而设计的包装基类。
- 避坑指南:如果你想自己写一个类来实现一种特殊的字典,直接去继承 Python 原生的
dict极其容易踩坑(因为原生字典底层是用 C 语言写的,它自带的许多内置方法不会触发你重写的逻辑)。 - 因此,官方推荐:只要你想自定义字典,就继承
UserDict!它的内部把真正的字典数据存在了一个叫做data的属性里,修改起来非常安全。 - 知识联动:Hugging Face 分词器返回的那个强大且好用的
BatchEncoding对象,其底层就是直接继承自UserDict的!
(1).UserDict() - 字典自定义基类
作用:作为基类被继承,用于派生出带有自定义逻辑的特殊字典对象。开发者通过重写魔术方法(如 __setitem__、__getitem__),可以轻松拦截、监听或修改字典的读写行为。
from collections import UserDict
class MyCustomDict(UserDict):
pass
参数:
- 初始化数据 (dict, kwargs): 实例化时支持像普通字典一样传入键值对,这些数据会被自动保存在对象的
self.data属性中。
返回值:
- 成功: 返回一个具有完全字典行为,且支持安全定制的自定义对象。
示例:
from collections import UserDict
# 需求:写一个特殊的字典,用户存入的任何 Key 都会被强制变成大写
class UpperKeyDict(UserDict):
# 重写赋值方法 (拦截 dict[key] = value 的操作)
def __setitem__(self, key, value):
# 强制把 key 转大写后,存入底层的真实字典 self.data 中
self.data[key.upper()] = value
# 测试我们自己手搓的字典
my_dict = UpperKeyDict()
my_dict['apple'] = 100
my_dict['Banana'] = 200
print(my_dict)
# 结果: {'APPLE': 100, 'BANANA': 200} (所有的 Key 都自动变大写了!)
# 查看底层的真实数据载体
print(type(my_dict.data))
# 结果: <class 'dict'>
(2).data 属性 - 获取底层原生字典
作用:这是 UserDict 暴露出来的核心属性。当你想把包装后的字典当作最纯粹的 Python 字典传递给其他 C 语言底层库(如 json.dumps)时,直接传入 self.data 可以避免很多序列化错误。
示例:
import json
my_dict = UpperKeyDict()
my_dict['apple'] = 100
# 推荐:获取底层真实字典再进行序列化
print(json.dumps(my_dict.data))
# 结果: {"APPLE": 100}
三. collections中列表相关类型(list)
1. deque类型
deque不是任何其他原生基本数据结构的子类!
deque是一种双端队列。
避坑指南:如果需要频繁在列表头部插入或删除数据,原生的 list!原生列表在头部操作的时间复杂度是 O(N)O(N)O(N)(因为要移动其后所有的元素),数据量大时会严重卡顿。而 deque 在两端操作的时间复杂度均为 O(1)O(1)O(1),是解决此类性能痛点的完美替代品。
(1).deque() - 双端队列实例
作用:创建一个类似列表的容器,支持在两端高效、线程安全地追加和弹出数据。常用于滑动窗口、保留最近 N 条历史记录、以及算法中的广度优先搜索 (BFS)。
from collections import deque
deque([iterable[, maxlen]])
参数:
- 可迭代对象 (iterable): 可选。用于初始化队列的数据源(如列表、字符串等)。
- 最大长度 (maxlen): 可选且非常核心的参数!指定队列的最大长度。如果指定了
maxlen,当队列满了之后再加入新元素,另一端的旧元素会被自动挤出(丢弃)。如果不指定,队列则可以无限增长。
返回值:
- 成功: 返回一个
deque类型的实例对象,支持类似列表的索引访问,且两端操作极快。
示例:
from collections import deque
# 1. 基础两端高效操作
d = deque(['b', 'c'])
d.append('d') # 尾部插入
d.appendleft('a') # 头部插入 (比 list.insert(0, 'a') 性能高极多!)
print(d)
# 结果: deque(['a', 'b', 'c', 'd'])
# 2. 限制最大长度 (maxlen) 的妙用:保留最近的 N 条记录
# 例如构建一个大小为 3 的滑动窗口
history = deque(maxlen=3)
history.append(1)
history.append(2)
history.append(3)
print(history)
# 结果: deque([1, 2, 3], maxlen=3)
# 继续插入新元素 4,最左侧最旧的 '1' 会被自动挤出去
history.append(4)
print(history)
# 结果: deque([2, 3, 4], maxlen=3)
(2).append() 与 pop() - 尾插/尾删
作用:在双端队列的尾部(最右侧)执行插入和弹出。由于这是最常规的数据操作方向,官方为了兼容原生 list 的代码习惯,没有给它们加 right 后缀,而是直接沿用了原生的命名。
示例:
d = deque(['a', 'b'])
d.append('c') # 尾部插入
print(d) # 结果: deque(['a', 'b', 'c'])
right_val = d.pop() # 尾部高效弹出
print(right_val) # 结果: 'c'
(3).appendleft() 与 popleft() - 头插/头删
作用:在双端队列的头部(最左侧)执行插入和弹出,这是替代原生 list.insert(0, x) 和 list.pop(0) 的绝对主力方法。
示例:
d = deque(['b', 'c'])
d.appendleft('a') # 头部插入
print(d) # 结果: deque(['a', 'b', 'c'])
left_val = d.popleft() # 头部弹出
print(left_val) # 结果: 'a'
(4).extendleft() - 头部批量插入
作用:将另一个可迭代对象的元素批量插入到队列头部。注:由于它是将元素依次插入头部,最终队列中的元素顺序会和传入的顺序正好相反!
示例:
d = deque([4, 5])
d.extendleft([1, 2, 3])
print(d)
# 结果: deque([3, 2, 1, 4, 5]) # 注意 1,2,3 的顺序反转了!
(5).rotate() - 轮转队列
作用:将队列中的所有元素向右(或向左)循环移动指定的步数。无需你自己手写切片重组代码,常用于算法题中的轮转数组、字符串移位。
deque.rotate(n=1)
参数:
- 步数 (n): 传入正数,向右循环移动;传入负数,向左循环移动。
示例:
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])
四.collections中元组相关类型(tuple)
1. namedtuple类型 (元组的类工厂函数)
==说明:namedtuple 本质上是一个“用来生成元组子类的工厂函数”。==普通的 tuple 只能通过数字下标(如 person[0])来访问元素,代码可读性极差。而 namedtuple 允许你为元组的每个位置赋予一个“名字”(字段名),让你既能像原生元组一样享受高内存效率和不可变性,又能像对象一样通过点语法(如 person.name)优雅地访问数据。
==注:==为什么 namedtuple 的专属方法都带有前缀下划线(如 _make, _asdict)?在 Python 中,下划线通常代表私有方法,但这里是个例外!官方故意加下划线,是为了防止你定义的“字段名”和这些自带方法的名字发生冲突。比如你的元组里恰好有一个字段叫 make,如果自带方法叫 make() 就会乱套,所以官方将其命名为 _make()。
(1). namedtuple() - 定义并创建具名元组类
作用:工厂函数。通过传入类名和字段名列表,当场“生产”出一个专属的元组子类。
from collections import namedtuple
namedtuple(typename, field_names)
参数:
- 类名 (typename): 字符串。你想要创建的新元组子类的名字。
- 字段名 (field_names): 可以是由字符串组成的列表
['x', 'y'],也可以是用空格/逗号隔开的单字符串"x y"。
返回值:
- 成功: 返回一个全新的、定制化的“元组子类”,你可以用它去实例化具体的数据对象。
示例:
from collections import namedtuple
# 1. 用工厂函数生产一个叫做 'Point' 的类,包含 x 和 y 两个坐标字段
Point = namedtuple('Point', ['x', 'y'])
# 2. 像使用普通类一样,实例化一个坐标点对象
p = Point(11, y=22)
# 3. 核心优势:通过字段名直接访问,代码极具可读性!
print(p.x, p.y)
# 结果: 11 22
# 4. 同时完美兼容普通元组的特性 (支持解包和数字索引)
x_val, y_val = p
print(p[0], x_val)
# 结果: 11 11
(2). _make() - 批量转化具名元组
作用:这是一个类方法。它可以直接将一个现有的可迭代对象(如列表、元组)批量包装、实例化为一个具名元组对象。
NewClass._make(iterable)
参数:
- 可迭代对象 (iterable): 包含具体数据的列表、元组等,其元素数量必须与具名元组的字段数完全一致。
示例:
from collections import namedtuple
Point = namedtuple('Point', ['x', 'y'])
# 假设我们收到了数据库或表格传来的一行数据列表
data_list = [100, 200]
# 使用 _make 快速把列表数据转化为 Point 对象
p_new = Point._make(data_list)
print(p_new)
# 结果: Point(x=100, y=200)
(3). _asdict() - 字典化映射
作用:将具名元组对象转换为一个高阶的 dict(在 Python 3.8+ 中返回普通 dict,在此之前返回 OrderedDict)。常用于将元组数据优雅地转化为 JSON 格式输出。
instance._asdict()
示例:
from collections import namedtuple
Point = namedtuple('Point', ['x', 'y'])
p = Point(x=5, y=10)
# 转化为字典结构
p_dict = p._asdict()
print(p_dict)
# 结果: {'x': 5, 'y': 10}
(4). _replace() - 字段值替换
作用:替换当前对象中指定字段的值。注:由于元组是不可变的,此操作并不会直接修改原对象,而是当场为你克隆并返回一个修改后的全新具名元组对象。
instance._replace(**kwargs)
示例:
p = Point(x=1, y=2)
# 尝试修改 x 的值(原对象 p 保持不变)
p_modified = p._replace(x=100)
print(p) # 原对象没变!结果: Point(x=1, y=2)
print(p_modified) # 生成了新对象!结果: Point(x=100, y=2)
更多推荐


所有评论(0)