Python库collections详解 (一)
模块概述
模块作用
这个模块实现了一些专门化的容器,提供了对 Python 的通用内建容器 dict、list、set 和 tuple 的补充。
官方资料
collections --- 容器数据类型 — Python 3.13.2 文档
子类概览
namedtuple() | 创建命名元组子类的工厂函数,生成可以使用名字来访问元素内容的tuple子类 |
deque | 类似列表(list)的容器,实现了在两端快速添加(append)和弹出(pop) |
ChainMap | 类似字典(dict)的容器类,将多个映射集合到一个视图里面 |
Counter | 字典的子类,提供了可哈希对象的计数功能 |
OrderedDict | 字典的子类,保存了他们被添加的顺序,有序字典 |
defaultdict | 字典的子类,提供了一个工厂函数,为字典查询提供一个默认值 |
UserDict | 封装了字典对象,简化了字典子类化 |
UserList | 封装了列表对象,简化了列表子类化 |
UserString | 封装了字符串对象,简化了字符串子类化(中文版翻译有误) |
Counter--计数器
一个计数器工具,为的是可以方便快速地计账。例如可以利用Counter分析列表中不同单词的出现次数
ls = ['y','n','n','y','y','n']
cnt = Counter()
for ch in ls:
cnt[ch] += 1
cnt
# 结果 Counter({'y': 3, 'n': 3})
初始化
c = Counter() # 一个新的空计数器
c = Counter('gallahad') # 从可迭代对象创建一个新的计数器
c = Counter({'red': 4, 'blue': 2}) # 从映射创建一个新的计数器
c = Counter(cats=4, dogs=8) # 从关键字参数创建一个新的计数器
使用
查询
在使用Counter进行查询时,格式和字典一样:c['a'],区别在于如果查询不到会返回0而不是KeyError
elements()
返回一个迭代器,每个元素出现其计数器的对应次数,同时出现顺序和原顺序相同
from collections import Counter
# 创建一个 Counter 对象
c = Counter(['apple', 'banana', 'apple', 'orange', 'banana', 'banana'])
# 获取元素的迭代器
elements = list(c.elements())
print(elements) # 输出: ['apple', 'apple', 'banana', 'banana', 'banana', 'orange']
most_common([n])
返回一个列表,其中包含 n 个最常见的元素及出现次数,按常见程度由高到低排序。 如果 n 被省略或为 None
,most_common()将返回计数器中的 所有 元素。 计数值相等的元素按首次出现的顺序排序
提供n
from collections import Counter
# 创建一个 Counter 对象
c = Counter(['apple', 'banana', 'apple', 'orange', 'banana', 'banana', 'kiwi'])
# 获取最常见的元素
most_common_elements = c.most_common(2) # 获取前 2 个最常见的元素
print(most_common_elements) # 输出: [('banana', 3), ('apple', 2)]
不提供n
# 获取所有最常见的元素
all_common_elements = c.most_common()
print(all_common_elements)
# 输出: [('banana', 3), ('apple', 2), ('orange', 1), ('kiwi', 1)]
subtract([iterable-or-mapping])
减去一个可迭代对象或者映射对象中的元素,输入和输出可以为负
c = Counter(a=4, b=2, c=0, d=-2)
d = Counter(a=1, b=2, c=3, d=4)
c.subtract(d)
c
Counter({'a': 3, 'b': 0, 'c': -3, 'd': -6})
total()
计算总数
c = Counter(a=10, b=5, c=0)
c.total()
15
update([iterable-or-mapping])
加上一个 可迭代对象 或 映射对象 (或 counter) 中的元素。
from collections import Counter
# 创建一个 Counter 对象
c = Counter(['apple', 'banana', 'apple'])
print("初始计数器:", c) # 输出: 初始计数器: Counter({'apple': 2, 'banana': 1})
# 使用可迭代对象更新计数器
c.update(['banana', 'kiwi', 'kiwi'])
print("更新后的计数器 (使用可迭代对象):", c)
# 输出: 更新后的计数器 (使用可迭代对象): Counter({'apple': 2, 'banana': 2, 'kiwi': 2})
# 使用字典更新计数器
c.update({'apple': 1, 'kiwi': 3})
print("更新后的计数器 (使用字典):", c)
# 输出: 更新后的计数器 (使用字典): Counter({'kiwi': 5, 'apple': 3, 'banana': 2})
常见字典方法都能使用
c.total() # 所有计数的总和
c.clear() # 重置所有计数
list(c) # 列出不同的元素
set(c) # 转换为集合
dict(c) # 转换为常规字典
c.items() # 访问 (元素, 计数) 对
Counter(dict(list_of_pairs)) # 从 (元素, 计数) 对的列表转换
c.most_common()[:-n-1:-1] # n 个最不常见的元素
+c # 移除计数为零和负的元素
deque--双向队列
返回一个新的双向队列对象
list对象也支持类似的操作,但它们是针对快速的固定长度的操作进行优化而 pop(0)
和 insert(0, v)
操作对下层数据表示的大小和位置改变都将产生 O(n) 的内存移动开销。
初始化
d = deque('ghi')
使用
append(x)
添加 x 到右端。
from collections import deque
d = deque()
d.append(1)
d.append(2)
print(d) # 输出: deque([1, 2])
appendleft(x)
添加 x 到左端。
d.appendleft(0)
print(d) # 输出: deque([0, 1, 2])
clear()
移除所有元素,使其长度为0.
copy()
创建一份浅拷贝。
count(x)
计算 deque 中元素等于 x 的个数。
d = deque([1, 2, 2, 3])
count_of_2 = d.count(2)
print(count_of_2) # 输出: 2
extend(iterable)
扩展deque的右侧,通过添加iterable参数中的元素。
d.extend([4, 5])
print(d) # 输出: deque([1, 2, 2, 3, 4, 5])
extendleft(iterable)
扩展deque的左侧,通过添加iterable参数中的元素。注意,左添加时,在结果中iterable参数中的顺序将被反过来添加。
d.extendleft([-1, 0])
print(d) # 输出: deque([0, -1, 1, 2, 2, 3, 4, 5])
index(x[, start[, stop]])
返回 x 在 deque 中的位置(在索引 start 之后,索引 stop 之前)。 返回第一个匹配项,如果未找到则引发 ValueError。
index_of_2 = d.index(2)
print(index_of_2) # 输出: 2
insert(i, x)
在位置 i 插入 x 。
如果插入会导致一个限长 deque 超出长度 maxlen 的话,就引发一个 IndexError。
d.insert(1, 1.5)
print(d) # 输出: deque([0, 1.5, -1, 1, 2, 2, 3, 4, 5])
pop()
移去并且返回一个元素,deque 最右侧的那一个。 如果没有元素的话,就引发一个 IndexError。
last_element = d.pop()
print(last_element) # 输出: 5
print(d) # 输出: deque([0, 1.5, -1, 1, 2, 2, 3, 4])
popleft()
移去并且返回一个元素,deque 最左侧的那一个。 如果没有元素的话,就引发 IndexError。
first_element = d.popleft()
print(first_element) # 输出: 0
print(d) # 输出: deque([1.5, -1, 1, 2, 2, 3, 4])
remove(value)
移除找到的第一个 value。 如果没有的话就引发 ValueError。
d.remove(2)
print(d) # 输出: deque([1.5, -1, 1, 2, 3, 4])
reverse()
将deque逆序排列。返回 None
。
rotate(n=1)
向右循环移动 n 步。 如果 n 是负数,就向左循环。
如果deque不是空的,向右循环移动一步就等价于 d.appendleft(d.pop())
, 向左循环一步就等价于 d.append(d.popleft())
。
d.rotate(2)
print(d) # 输出: deque([1, -1, 1.5, 4, 3, 2])
在上述操作以外,deque 还支持迭代, 封存, len(d)
, reversed(d)
, copy.copy(d)
, copy.deepcopy(d)
, 使用 in 运算符的成员检测以及下标引用例如通过 d[0]
访问首个元素等。
有序字典--OrderDict
有序词典就像常规词典一样,但有一些与排序操作相关的额外功能。由于内置的 dict 类获得了记住插入顺序的能力(Python 3.7 ),它们变得不那么重要了。
初始化
from collections import OrderedDict
# 1. 无参数初始化
od1 = OrderedDict()
print(od1) # 输出: OrderedDict()
# 2. 使用可迭代对象初始化
od2 = OrderedDict([('key1', 'value1'), ('key2', 'value2')])
print(od2) # 输出: OrderedDict([('key1', 'value1'), ('key2', 'value2')])
# 3. 使用字典初始化
od3 = OrderedDict({'key1': 'value1', 'key2': 'value2'})
print(od3) # 输出: OrderedDict([('key1', 'value1'), ('key2', 'value2')])
# 4. 使用关键字参数初始化
od4 = OrderedDict(key1='value1', key2='value2')
print(od4) # 输出: OrderedDict([('key1', 'value1'), ('key2', 'value2')])
使用
popitem(last = True)
移除并返回一个 (key, value) 键值对。 如果 last 值为真,则按 LIFO 后进先出的顺序返回键值对,否则就按 FIFO 先进先出的顺序返回键值对。
from collections import OrderedDict
d = OrderedDict.fromkeys('abcde')
d.popitem()
# ('e', None)
d
# OrderedDict([('a', None), ('b', None), ('c', None), ('d', None)])
#last=False时,弹出第一个
d = OrderedDict.fromkeys('abcde')
''.join(d.keys())
# 'abcde'
d.popitem(last=False)
''.join(d.keys())
# 'bcde'
move_to_end
from collections import OrderedDict
d = OrderedDict.fromkeys('abcde')
p = ''.join(d.keys())
print(p)
# 'acdeb'
d.move_to_end('b', last=False)
''.join(d.keys())
print(p)
# 'bacde'
reversed
d = OrderedDict.fromkeys('abcde')
list(reversed(d))
['e', 'd', 'c', 'b', 'a']
可命名元组-namedtuple
生成可以使用名字来访问元素内容的tuple子类,命名元组赋予每个位置一个含义,提供可读性和自文档性。它们可以用于任何普通元组,并添加了通过名字获取值的能力,通过索引值也是可以的。
初始化
namedtuple(typename,field_names,*,verbose=False, rename=False, module=None)
1)typename:该参数指定所创建的tuple子类的类名,相当于用户定义了一个新类。
2)field_names:该参数是一个字符串序列,如 ['x','y']。此外,field_names 也可直接使用单个字符串代表所有字段名,多个字段名用空格、逗号隔开,如 'x y' 或 'x,y'。任何有效的 Python 标识符都可作为字段名(不能以下画线开头)。有效的标识符可由字母、数字、下画线组成,但不能以数字、下面线开头,也不能是关键字(如 return、global、pass、raise 等)。
3)rename:如果将该参数设为 True,那么无效的字段名将会被自动替换为位置名。例如指定 ['abc','def','ghi','abc'],它将会被替换为 ['abc', '_1','ghi','_3'],这是因为 def 字段名是关键字,而 abc 字段名重复了。
4)verbose:如果该参数被设为 True,那么当该子类被创建后,该类定义就被立即打印出来。
5)module:如果设置了该参数,那么该类将位于该模块下,因此该自定义类的 __module__ 属性将被设为该参数值。
案例
使用 namedtuple
创建一个表示学生的类
from collections import namedtuple
# 定义一个名为 Student 的 namedtuple,包含字段 name, age 和 grade
Student = namedtuple('Student', ['name', 'age', 'grade'])
# 创建几个学生实例
student1 = Student(name='Alice', age=20, grade='A')
student2 = Student(name='Bob', age=22, grade='B')
student3 = Student(name='Charlie', age=21, grade='A')
# 打印学生信息
print(student1) # 输出: Student(name='Alice', age=20, grade='A')
print(student2) # 输出: Student(name='Bob', age=22, grade='B')
print(student3) # 输出: Student(name='Charlie', age=21, grade='A')
# 访问字段
print(f"{student1.name} is {student1.age} years old and received a grade of {student1.grade}.")
# 输出: Alice is 20 years old and received a grade of A.
使用
_make(iterable)
类方法从存在的序列或迭代实例创建一个新实例。
# 使用 _make() 方法创建一个新的 Student 实例
student_data = ['David', 23, 'A-']
student4 = Student._make(student_data)
print(student4)
# 输出: Student(name='David', age=23, grade='A-')
_asdict()
返回一个新的 dict ,它将字段名称映射到它们对应的值:
# 使用 _asdict() 方法将 namedtuple 转换为字典
student_dict = student1._asdict()
print(student_dict)
# 输出: {'name': 'Alice', 'age': 20, 'grade': 'A'}
_replace(**kwargs)
返回一个新的命名元组实例,并将指定域替换为新的值
# 使用 _replace() 方法创建一个新的实例,修改某个字段
student1_updated = student1._replace(grade='A+')
print(student1_updated)
# 输出: Student(name='Alice', age=20, grade='A+')