大家好,我是赛博红兔。大伙有没有遇到过这样的问题:在日常开发中,需要用字典来统计数据频率,却发现自己写的代码太长而且效率很低?或者需要在列表前后快速插入和删除元素,但普通的列表性能不够理想?这个时候,collections 模块就可以派上用场了!collections 是 Python 提供的一个标准库模块,它扩展了内建容器数据类型(比如列表、字典、元组等)的功能,让我们更方便地解决许多实际问题。
collections 模块最常用的是以下几种容器:
Counter:用于计数的哈希表
deque:高效的双端队列
defaultdict:带默认值的字典
OrderedDict:有序字典
namedtuple:命名元组
下面,我们一个一个来看。
- Counter类
Counter 是一个专门用于统计数据的工具,返回一个类似字典的对象,其中键是被统计的元素,值是对应的出现次数。首先,我们有一个包含水果名称的列表 data,其中有重复的元素,比如 ‘apple’ 和 ‘banana’。接着,我们用 Counter(data) 来创建一个计数器对象。它会自动统计每个元素的出现次数,并存储在一个类似字典的对象中。这里我们用 most_common(2) 方法来获取计数器中出现次数最多的2个元素及其次数。这在需要统计前几名数据时特别方便,比如找出最受欢迎的商品、最常用的单词等等。update 方法用来更新计数器的统计结果。在这里,我们传入了一个新列表 [‘banana’, ‘banana’, ‘grape’],它会把列表中的每个元素计数加到原来的结果中。通过 del 语句,我们可以从计数器中删除某个元素及其统计信息。比如这里,我们删除了 ‘apple’,计数器中再也找不到这个键了。这里的交集 & 会对两个 Counter 中每个元素的计数取最小值。这里的并集 | 会对两个 Counter 中每个元素的计数取最大值。
from collections import Counter, deque, defaultdict
from collections import OrderedDict, namedtuple
data = ['apple', 'banana', 'apple', 'orange', 'banana', 'apple']
counter = Counter(data)
print("统计结果:", counter)
print("最常见的元素:", counter.most_common(2))
counter.update(['banana', 'banana', 'grape'])
print("更新后的统计:", counter)
del counter['apple']
print("删除后的计数器:", counter)
counter1 = Counter(a=3, b=1)
counter2 = Counter(a=1, b=2, c=3)
print("交集:", counter1 & counter2)
print("并集:", counter1 | counter2)
- deque类
deque 是双端队列(double-ended queue),支持在两端快速插入和删除。首先,我们用 deque 创建了一个队列,里面有三个元素:a, b, 和 c。这个队列是一个双端队列,什么意思呢?它支持从两边插入和删除,非常灵活!打印一下,我们能看到当前队列的状态。这里我们用了两个操作,append(‘d’) 是在队列的 右端 插入一个新元素 d。appendleft(‘z’) 是在队列的 左端 插入一个新元素 z。插入后,队列的状态变成了这样,可以看到两边都加了新元素。接着,我们从两边删除元素,pop() 是从 右端 删除一个元素,删除的是 d。popleft() 是从 左端 删除一个元素,删除的是 z。删完之后,队列又回到了中间部分。extend([‘e’, ‘f’]) 是把 e 和 f 加到队列的 右端,相当于批量插入。rotate(2) 是一个非常有趣的操作。它会把队列里的元素整体 向右旋转两步。旋转之后,最后的两个元素 e 和 f 被移到了最前面。这部分讲的是队列的一个高级功能:限制长度。我们用 deque(maxlen=3) 创建了一个队列,这个队列最多只能存 3 个元素。用 extend([1, 2, 3]) 把 1、2、3 加进去,这时队列是满的:deque([1, 2, 3], maxlen=3)。然后我们 append(4),加入一个新元素 4。由于队列的长度已经满了,所以最左边的元素 1 被自动移除了。
from collections import deque
dq = deque(['a', 'b', 'c'])
print("初始队列:", dq)
dq.append('d')
dq.appendleft('z')
print("插入后的队列:", dq)
dq.pop()
dq.popleft()
print("删除后的队列:", dq)
dq.extend(['e', 'f'])
dq.rotate(2)
print("旋转后的队列:", dq)
dq_limited = deque(maxlen=3)
dq_limited.extend([1, 2, 3])
dq_limited.append(4)
print("有限长度队列:", dq_limited)
- defaultdict类
defaultdict 是一个带有默认值的字典,可以在访问不存在的键时返回默认值,而不是抛出 KeyError。我们创建了一个 defaultdict,并指定了它的默认值类型为 int,它的默认返回值是 0。也就是说,如果我们访问一个不存在的键,这个键会自动初始化为 0。我们有一个列表 data,其中包含了一些水果的名字。现在我们想统计每种水果出现的次数。如果这个水果名在字典 dd 里已经有了值,就加 1。如果没有,defaultdict 会自动给这个键赋值为默认值,也就是 0,然后加 1。用普通字典的话,还需要先判断键是否存在,但用 defaultdict 省去了这个麻烦。我们把统计好的 dd 打印出来,它会像字典一样展示键值对。这里我们创建了另一个 defaultdict,但这次默认值是一个空列表。如果访问的键不存在,就会自动给这个键赋值为一个空列表。现在我们向键 ‘key’ 对应的列表里添加一个值 ‘value’。如果 ‘key’ 不存在,defaultdict 会自动创建它,并赋值为一个空列表。然后我们用 append 方法往这个列表里添加值。普通字典在这种情况下会报错,但 defaultdict 自动帮我们处理了。
from collections import defaultdict
dd = defaultdict(int)
data = ['apple', 'banana', 'apple']
for item in data:
dd[item] += 1
print("统计结果:", dd)
dd_list = defaultdict(list)
dd_list['key'].append('value')
print("列表默认值:", dd_list)
- OrderedDict类
OrderedDict 是一个有序字典,能记住插入键值对的顺序。
我们先创建了一个OrderedDict对象,名字叫od。我们可以看到它会记住插入元素的顺序。如果是普通的字典,从Python 3.7开始,插入顺序也会被保留,但OrderedDict更强调顺序的功能,在一些特殊场景更适用。接下来,我们想把这个字典按照键的字母顺序重新排序,比如让a、b、c按照字母顺序排列。打印结果你会发现,现在的顺序是:a -> b -> c。然后,我们又做了一件事:按照值的大小排序,比如让1、2、3按升序排列。打印出来的结果,这次顺序变成了:b -> c -> a,因为值1对应的是b,值2对应的是c,值3对应的是a。
from collections import OrderedDict
od = OrderedDict()
od['a'] = 3
od['c'] = 2
od['b'] = 1
print("有序字典:", od)
od_sorted = OrderedDict(sorted(od.items(), key=lambda x: x[0]))
print("按键排序后的字典:", od_sorted)
od_sorted_by_value = OrderedDict(sorted(od.items(), key=lambda x: x[1]))
print("按值排序后的字典:", od_sorted_by_value)
- namedtuple类
namedtuple 是一种增强型的元组,支持字段名称访问,并且保持了元组的不可变特性。这里我们用 namedtuple 定义了一个叫 Point 的“命名元组”。我们定义了它的“x 坐标”和“y 坐标”。然后我们用 Point(10, 20) 创建了一个实例,表示一个点的坐标是 (10, 20)。打印结果时,它会清楚地告诉你这个点的两个坐标值。命名元组的特别之处就是字段可以通过名字访问,就像访问类的属性一样。这种字段名称的访问方式,代码可读性很高,也比通过索引访问直观多了。_asdict() 是命名元组自带的方法,可以把它转换成普通字典。这种格式很方便,因为字典可以用在很多地方,比如存储数据或者和 JSON 互相转换。_replace() 是命名元组的另一个实用方法,用来创建一个新的命名元组,替换掉指定字段的值。这里的 p._replace(x=100) 意思是把点的 x 坐标改为 100,而 y 坐标保持不变。注意,这个方法不会修改原来的命名元组 p,因为命名元组是不可变的(类似字符串),所以必须返回一个新的实例。
from collections import namedtuple
Point = namedtuple('Point', ['x', 'y'])
p = Point(10, 20)
print("坐标:", p)
print("x 坐标:", p.x)
print("y 坐标:", p.y)
print("转换为字典:", p._asdict())
p_new = p._replace(x=100)
print("替换后的命名元组:", p_new)
最后来总结一下,我们可以看到 collections 模块能显著提高 Python 编程的效率,同时让代码更清晰简洁。这些工具涵盖了从数据统计、队列操作到字典增强、元组扩展的方方面面,是我们开发中不可或缺的宝藏模块。好啦,本频道将同步在B站和油管上更新,如果你喜欢我的内容,请不要忘记点赞、订阅和分享,这样就不会错过我更新的内容,欢迎在评论区提出你的想法和建议。今天就到这里,再见吧!

Leave a comment