python中的 collections 模块(用法、详解、底层原理,示例等)
1、collections 模块中的 defaultdict
1.1 defaultdict 功能
可以设置一个默认值作为字典中新key的默认值。该默认值可以是任何对象, 包括函数、列表、元组、集合等。默认值不需要像dict那样事先定义,因为它在需要的时候会自动创建
使用defaultdict,可以简化代码并提高代码的可读性,而且可以防止KeyError异常的出现。同时,defaultdict的性能与普通字典相当,因为底层实现并不影响字典的性能。
1.2 底层原理
重写了字典的__missing__方法。当访问字典中不存在的key时,__missing__方法负责返回默认值。因此,defaultdict通过继承dict,重载了__missing__方法,以提供默认值。
1.3 defaultdict 源码
class defaultdict(dict):"""defaultdict(default_factory[, ...]) --> dict with default factoryThe default factory is called without arguments to producea new value when a key is not present, in __getitem__ only.A defaultdict compares equal to a dict with the same items.All remaining arguments are treated the same as if they werepassed to the dict constructor, including keyword arguments."""def copy(self): # real signature unknown; restored from __doc__""" D.copy() -> a shallow copy of D. """passdef __copy__(self, *args, **kwargs): # real signature unknown""" D.copy() -> a shallow copy of D. """passdef __getattribute__(self, *args, **kwargs): # real signature unknown""" Return getattr(self, name). """passdef __init__(self, default_factory=None, **kwargs): # known case of _collections.defaultdict.__init__"""defaultdict(default_factory[, ...]) --> dict with default factoryThe default factory is called without arguments to producea new value when a key is not present, in __getitem__ only.A defaultdict compares equal to a dict with the same items.All remaining arguments are treated the same as if they werepassed to the dict constructor, including keyword arguments.# (copied from class doc)"""passdef __missing__(self, key): # real signature unknown; restored from __doc__"""__missing__(key) # Called by __getitem__ for missing key; pseudo-code:if self.default_factory is None: raise KeyError((key,))self[key] = value = self.default_factory()return value"""passdef __reduce__(self, *args, **kwargs): # real signature unknown""" Return state information for pickling. """passdef __repr__(self, *args, **kwargs): # real signature unknown""" Return repr(self). """passdefault_factory = property(lambda self: object(), lambda self, v: None, lambda self: None) # default"""Factory for default value called by __missing__()."""
1.4 简单使用和解释以及举例
# ======================================================
from collections import defaultdictdata_dic = defaultdict(list) # default_factory默认是None, 这里default_factory默认是None赋值为列表
data_dic['name'].append("java")
data_dic['name'].append("python")
data_dic['name'].append("go")
data_dic['test_num'].append(1)
data_dic['test_num'].append(2)# 虽然default_factory是list,但是它并不限制data_dic中键的类型,可以添加任何类型的键和值
data_dic['category'] = "IT"print(data_dic)
# 打印的是一个defaultdict的对象,和字典一样的用法,也可以用 dict(data_dic) 将其转化为字典,但没必要,defaultdict对象和字典一样的用法
# 打印结果:defaultdict(<class 'list'>, {'name': ['java', 'python', 'go'], 'test_num': [1, 2], 'category': 'IT'})
print(data_dic['category'])# data_dic中还没有 age 这个键,那么default_factory就会自动生成一个空列表作为该键的值
print(data_dic['age'])
2、collections 模块中的 OrderedDict
2.1 OrderedDict 功能
OrderedDict是Python中collections模块中的一种字典,它可以按照元素添加的顺序来存储键值对,保证了元素的顺序性。 与Python中普通字典不同的是,OrderedDict中的元素顺序是有序的,而且支持所有字典的操作。
OrderedDict类的优点是能够维护元素的有序性,缺点是存储元素时需要使用双向链表,因此需要比字典类占用更多的内存空间
字典所拥有的方法 OrderedDict 也同样拥有
2.2 OrderedDict 底层原理
OrderedDict底层原理是通过维护一个双向链表来实现的,该链表按照元素添加的顺序来存储键值对。
除了维护一个双向链表外,OrderedDict还维护了一个字典,在字典中存储了键值对的映射关系,以支持快速查找和删除操作。
因此,OrderedDict的底层实现是比较复杂的,但是它可以保证元素的顺序性,并且支持字典的所有操作。每个元素都是一个节点,包含该元素的值、键和哈希值。每个节点还维护了两个指针,分别指向前一个节点和后一个节点。
当向OrderedDict中添加元素时,新元素将被添加到链表的末尾,并成为新的尾节点。
如果添加的元素已经存在于OrderedDict中,那么该元素将被删除然后被重新添加到链表的末尾,从而保证了元素的有序性。
2.3 OrderedDict 示例
- 有序字典,按照元素添加顺序打印键值对
from collections import OrderedDictd = OrderedDict()
d['python'] = 1
d['java'] = 2
d['go'] = 3
d['php'] = 4for key, value in d.items():print(key, value)# 打印结果
# python 1
# java 2
# go 3
# php 4
- 用 OrderedDict 实现先进先出的队列
from collections import OrderedDictclass FIFO(OrderedDict):def __init__(self, capacity):super().__init__()self.capacity = capacitydef __setitem__(self, key, value):if len(self) >= self.capacity: # 容量已满self.popitem(last=False) # 删除最早的元素,即队首元素super().__setitem__(key, value)q = FIFO(3)
q['a'] = 1
q['b'] = 2
q['c'] = 3
print(q) # 输出:FIFO([('a', 1), ('b', 2), ('c', 3)])
q['d'] = 4
print(q) # 输出:FIFO([('b', 2), ('c', 3), ('d', 4)])
定义了一个FIFO类,该类继承了OrderedDict,并重载了__setitem__方法,实现了一个先进先出的队列。
当队列已满时,每次添加新元素时,会删除最早添加的元素,从而使队列长度保持不变。
在代码中,当队列长度已经达到最大长度时,通过调用self.popitem(last=False)方法删除最早添加的元素,
即队首元素,从而腾出位置来添加新元素。
- popitem()
popitem()方法用于弹出最后插入的元素。它接受一个可选的参数last,用于指定弹出的是最后插入的元素还是第一个插入的元素。
from collections import OrderedDictordered_dict = OrderedDict({'a': 1, 'c': 3, 'b': 2})
print(ordered_dict.popitem()) # ('b', 2)
print(ordered_dict.popitem(last=False)) # ('a', 1)
- move_to_end()
move_to_end()方法可以将OrderedDict中的某个元素移动到双向链表的尾部或头部。
它接受两个可选参数,一个是key,指定要移动的元素;另一个是last,指定移动方向,
如果last为True,则移动到链表的末尾;
如果last为False,则移动到链表的头部。
from collections import OrderedDictordered_dict = OrderedDict({'a': 1, 'c': 3, 'b': 2})
ordered_dict.move_to_end('a') # 将'a'元素移动到OrderedDict的末尾
print(list(ordered_dict.keys())) # ['c', 'b', 'a']
ordered_dict.move_to_end('a', last=False) # 将'a'元素移动到OrderedDict的头部
print(list(ordered_dict.keys())) # ['a', 'c', 'b']
3、collections 模块中的 Counter
3.1 Counter 功能
- Counter类型可以接受一个可迭代对象作为参数,并对其中的元素进行计数。同时,还可以接受一个字典对象作为参数,用于初始化计数器。
from collections import Counter# 通过可迭代对象初始化计数器
c1 = Counter('hello')
print(c1) # Counter({'l': 2, 'h': 1, 'e': 1, 'o': 1})# 通过字典对象初始化计数器
c2 = Counter({'red': 4, 'blue': 2})
print(c2) # Counter({'red': 4, 'blue': 2})
- Counter类型还提供了一些有用的方法,例如:
elements():返回一个迭代器,其中包含每个元素的重复次数。如果重复次数为0或负数,则不会返回该元素。
most_common([n]):返回一个包含n个最常见元素及其计数的列表。如果n为空,则返回所有元素及其计数的列表。
subtract([iterable-or-mapping]):从计数器中减去指定的元素或计数器。这个方法会修改计数器本身。
from collections import Counterc = Counter('hello')
print(list(c.elements())) # ['h', 'e', 'l', 'l', 'o']print(c.most_common(2)) # [('l', 2), ('h', 1)]c.subtract('world')
print(c) # Counter({'l': 1, 'h': 1, 'e': 1, 'o': 1, 'w': -1, 'r': -1, 'd': -1})
3.2 Counter 底层原理
Counter类型的底层实现是基于哈希表的。在初始化计数器时,它会创建一个空的哈希表,并将可迭代对象中的元素作为键,计数作为值,存储到哈希表中。
在对计数器进行修改时,它会直接在哈希表中查找相应的元素,并更新其计数值。
这种基于哈希表的实现方式,使得Counter类型在计数操作时具有很高的效率和灵活性。
例如,它可以对任意可哈希对象进行计数,包括数字类型、字符串、元组等。
3.3 Counter 优缺点
- Counter类型的优点是:
可以对任意可哈希对象进行计数。
提供了一些有用的方法,方便对计数器进行操作。
在底层实现上,使用了哈希表,具有很高的效率和灵活性。
- Counter类型的缺点是:
由于底层实现依赖于哈希表,因此在计数器比较大时,可能会占用较大的内存空间。
在计数器上进行一些复杂的操作,比如求差集、并集等,可能会比较困难。
3.4 Counter 示例
- elements()
elements()方法会返回一个迭代器,其中包含每个元素的重复次数。如果重复次数为0或负数,则不会返回该元素。
from collections import Counter# 计算字符串中每个字符出现的次数,并按照出现次数升序输出
c = Counter('hello')
for element in c.elements():print(element, end=' ') # h e l l o
most_common([n])
most_common([n])
方法会返回一个包含n
个最常见元素及其计数的列表。如果n
为空,则返回所有元素及其计数的列表。
from collections import Counter# 计算字符串中每个字符出现的次数,并输出出现次数最多的前两个字符
c = Counter('hello')
print(c.most_common(2)) # [('l', 2), ('h', 1)]
- subtract([iterable-or-mapping])
subtract([iterable-or-mapping])方法会从计数器中减去指定的元素或计数器。这个方法会修改计数器本身。
from collections import Counter# 对字符串进行计数,并减去另一个字符串中出现的字符
c = Counter('hello')
c.subtract('world')
print(c) # Counter({'l': 1, 'h': 1, 'e': 1, 'o': 1, 'w': -1, 'r': -1, 'd': -1})
- update([iterable-or-mapping])
update([iterable-or-mapping])方法会将计数器更新为包括指定的元素或计数器。如果更新计数器后,某些元素的计数小于等于0,则会将这些元素从计数器中删除。
from collections import Counter# 对字符串进行计数,并更新为另一个字符串中出现的字符的计数
c = Counter('hello')
c.update('world')
print(c) # Counter({'l': 3, 'o': 2, 'h': 1, 'e': 1, 'w': 1, 'r': 1, 'd': 1})
- subtract()与update()的组合使用
通过subtract()方法可以将计数器中的元素减去指定的元素或计数器,而通过update()方法可以将计数器更新为包括指定的元素或计数器。这两个方法的组合使用,可以很方便地实现对计数器的增删操作。
from collections import Counter# 对字符串进行计数,并将其中的'h'和'o'删除
c = Counter('hello')
c.subtract('ho')
c += Counter() # 删除计数值为0或负数的元素
print(c) # Counter({'l': 2, 'e': 1})
- 使用Counter统计词频
# 统计test.txt中的词频
# 例如 test.txt 文件中的内容如下:
# python java c go
# java html vue
# c python go pythonfrom collections import Counterwith open('test.txt', 'r') as f:word_counts = Counter(f.read().split())print(word_counts.most_common(10))# 结果
# [('python', 3), ('java', 2), ('c', 2), ('go', 2), ('html', 1), ('vue', 1)]