【Python】collections模块

介绍

这个模块实现了一些专门化的容器,提供了对 Python 的通用内建容器 dict、list、set 和 tuple 的补充。

作用
namedtuple() 一个工厂函数,用来创建元组的子类,子类的字段是有名称的。
deque 类似列表的容器,但 append 和 pop 在其两端的速度都很快。
ChainMap 类似字典的类,用于创建包含多个映射的单个视图。
Counter 用于计数 hashable 对象的字典子类
OrderedDict 字典的子类,能记住条目被添加进去的顺序。
defaultdict 字典的子类,通过调用用户指定的工厂函数,为键提供默认值。
UserDict 封装了字典对象,简化了字典子类化
UserList 封装了列表对象,简化了列表子类化
UserString 封装了字符串对象,简化了字符串子类化

ChainMap

ChainMap 类将多个映射迅速地链到一起,这样它们就可以作为一个单元处理。这通常比创建一个新字典再重复地使用 update() 要快得多。

class collections.ChainMap(*maps)

基本用法

1
2
3
4
5
6
7
from collections import ChainMap

dict1 = {'a': 1, 'b': 2}
dict2 = {'b': 3, 'c': 4}

cm = ChainMap(dict1, dict2)
print(cm)  # ChainMap({'a': 1, 'b': 2}, {'b': 3, 'c': 4})

ChainMap 的键值查找规则

1
2
3
print(cm['a'])  # 1  (来自 dict1)
print(cm['b'])  # 2  (dict1 覆盖了 dict2 的 'b')
print(cm['c'])  # 4  (来自 dict2)
  • ChainMap 按照 从左到右(即 从第一个字典到最后一个字典)的顺序进行查找。
  • b 在 dict1 和 dict2 都存在,但 dict1 里的值 优先级更高。

如果查找一个不存在的键,会报 KeyError:

1
print(cm['d'])  # KeyError: 'd'

ChainMap 的修改规则

修改 ChainMap 只会影响第一个字典

1
2
3
cm['b'] = 99
print(dict1)  # {'a': 1, 'b': 99}
print(dict2)  # {'b': 3, 'c': 4}
  • 直接修改 cm[‘b’] 只会影响 dict1,不会影响 dict2。
  • ChainMap 只允许修改 第一个字典中的值。
  • 新增键值对也会加到第一个字典中

ChainMap 的动态特性

ChainMap 动态反映底层字典的变化:

1
2
dict1['a'] = 42
print(cm['a'])  # 42  (dict1 改变后,ChainMap 也会更新)

ChainMap 的方法

maps - 访问所有字典

1
print(cm.maps)  # [{'a': 42, 'b': 99, 'd': 100}, {'b': 3, 'c': 4}]

new_child() - 创建新的 ChainMap

1
2
cm2 = cm.new_child({'e': 5})
print(cm2)  # ChainMap({'e': 5}, {'a': 42, 'b': 99, 'd': 100}, {'b': 3, 'c': 4})
  • 这样 cm2 里 e 只会影响最前面的新字典,cm 不受影响。
  • 这在作用域管理中很有用(比如 Python 解释器的变量查找)。

ChainMap 的应用场景

配置管理

在应用程序中,我们通常有 默认配置,但也允许用户提供 自定义配置:

1
2
3
4
5
6
7
default_config = {'theme': 'light', 'font': 'Arial', 'timeout': 30}
user_config = {'theme': 'dark', 'timeout': 60}

config = ChainMap(user_config, default_config)

print(config['theme'])  # dark  (用户配置覆盖默认配置)
print(config['font'])   # Arial (用户未提供,使用默认值)

这样就能 优先使用用户配置,如果没有,就用默认值。

变量作用域

Python 解释器在查找变量时,会按 局部 -> 全局 -> 内置 的顺序进行查找,这与 ChainMap 类似:

1
2
3
4
5
6
7
global_scope = {'x': 10, 'y': 20}
local_scope = {'y': 5, 'z': 30}

env = ChainMap(local_scope, global_scope)
print(env['x'])  # 10  (来自全局作用域)
print(env['y'])  # 5   (局部作用域优先)
print(env['z'])  # 30  (来自局部作用域)

命令行参数解析

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
import argparse

# 定义命令行参数
parser = argparse.ArgumentParser()
parser.add_argument('--timeout', type=int)
parser.add_argument('--theme', type=str)

args = parser.parse_args([])  # 模拟不传递参数
cli_args = {k: v for k, v in vars(args).items() if v is not None}

# 默认配置
default_settings = {'theme': 'light', 'timeout': 30}
config = ChainMap(cli_args, default_settings)

print(config['theme'])  # light
print(config['timeout'])  # 30

defaultdict

defaultdict 是 Python collections 模块中的一个字典子类,它和普通的 dict 很相似,但有一个关键的区别:当访问不存在的键时,defaultdict 不会报 KeyError,而是返回一个默认值,这个默认值由一个工厂函数提供。

defaultdict 基本用法

1
2
3
4
5
6
7
from collections import defaultdict

# 创建一个默认值为 list 的 defaultdict
d = defaultdict(list)

# 访问不存在的键,返回一个新的空列表
print(d["key"])  # 输出:[]

对比普通字典:

1
2
d = {}
print(d["key"])  # ❌ KeyError

defaultdict 的参数

1
defaultdict(default_factory[, ...])
  • default_factory:用于生成默认值的可调用对象(如 list、int、set、lambda 等)。
  • 其他参数和 dict 一样。

常见默认工厂

默认值为 int(适用于计数器)

1
2
3
4
5
6
d = defaultdict(int)

d["apple"] += 1
d["banana"] += 1

print(d)  # {'apple': 1, 'banana': 1}

默认值为 list(适用于分组)

1
2
3
4
5
6
7
8
d = defaultdict(list)

d["fruits"].append("apple")
d["fruits"].append("banana")
d["vegetables"].append("carrot")

print(d)
# {'fruits': ['apple', 'banana'], 'vegetables': ['carrot']}

默认值为 set(去重分组)

1
2
3
4
5
6
7
8
d = defaultdict(set)

d["fruits"].add("apple")
d["fruits"].add("banana")
d["fruits"].add("apple")  # 重复添加,不会重复存储

print(d)
# {'fruits': {'apple', 'banana'}}

OrderedDict

OrderedDict 是 collections 模块中的有序字典,它继承自 dict,但能够保持键值对的插入顺序(在 Python 3.7+ 的 dict 也默认保持顺序,但 OrderedDict 仍有一些额外功能)

创建 OrderedDict

1
2
3
4
5
6
7
8
9
from collections import OrderedDict

# 普通 dict(Python 3.7+ 默认保持顺序)
d1 = {"a": 1, "b": 2, "c": 3}
print(d1)  # {'a': 1, 'b': 2, 'c': 3}

# OrderedDict(显式使用)
d2 = OrderedDict([("a", 1), ("b", 2), ("c", 3)])
print(d2)  # OrderedDict([('a', 1), ('b', 2), ('c', 3)])

特点:

  • Python 3.6 及以下:普通 dict 不会 维持插入顺序,OrderedDict 可以。
  • Python 3.7+:普通 dict 默认 维持顺序,但 OrderedDict 仍然提供额外方法。

OrderedDict 关键特性

按照插入顺序迭代

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
d = OrderedDict()
d["x"] = 10
d["y"] = 20
d["z"] = 30

for k, v in d.items():
    print(k, v)

# 输出:
# x 10
# y 20
# z 30  ✅ 顺序保持不变

支持 move_to_end 调整顺序

move_to_end(key, last=True) 可以移动指定键到末尾或开头:

1
2
3
4
5
6
7
d = OrderedDict([("a", 1), ("b", 2), ("c", 3)])

d.move_to_end("a")  # 把 'a' 移到末尾
print(d)  # OrderedDict([('b', 2), ('c', 3), ('a', 1)])

d.move_to_end("c", last=False)  # 把 'c' 移到开头
print(d)  # OrderedDict([('c', 3), ('b', 2), ('a', 1)])

支持 popitem()取出(FIFO/LIFO)

popitem(last=True):

  • last=True(默认)➡ 后进先出(LIFO)
  • last=False ➡ 先进先出(FIFO)
1
2
3
4
5
d = OrderedDict([("a", 1), ("b", 2), ("c", 3)])

print(d.popitem())       # ('c', 3)  ✅ 默认 LIFO
print(d.popitem(last=False))  # ('a', 1)  ✅ FIFO
print(d)  # OrderedDict([('b', 2)])

OrderedDict 应用场景

由于 OrderedDict 维护顺序,它可以实现 LRU(最近最少使用)缓存:

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
class LRUCache:
    def __init__(self, capacity: int):
        self.cache = OrderedDict()
        self.capacity = capacity

    def get(self, key):
        if key not in self.cache:
            return -1
        self.cache.move_to_end(key)  # 最近访问的移动到末尾
        return self.cache[key]

    def put(self, key, value):
        if key in self.cache:
            self.cache.move_to_end(key)
        self.cache[key] = value
        if len(self.cache) > self.capacity:
            self.cache.popitem(last=False)  # 移除最早插入的元素(FIFO)

# 示例
cache = LRUCache(2)
cache.put(1, "A")
cache.put(2, "B")
print(cache.cache)  # OrderedDict([(1, 'A'), (2, 'B')])

cache.get(1)  # 访问 1,使其变成最近使用
cache.put(3, "C")  # 淘汰最久未使用的 2
print(cache.cache)  # OrderedDict([(1, 'A'), (3, 'C')])
Licensed under CC BY-NC-SA 4.0
comments powered by Disqus
使用 Hugo 构建
主题 StackJimmy 设计