python 中的 collections 使用详解

1、collections 模块中的 defaultdict

1.1 defaultdict 功能

可以设置一个默认值作为字典中新key的默认值。该默认值可以是任何对象, 包括函数、列表、元组、集合等。默认值不需要像dict那样事先定义,因为它在需要的时候会自动创建
使用defaultdict,可以简化代码并提高代码的可读性,而且可以防止KeyError异常的出现。同时,defaultdict的性能与普通字典相当,因为底层实现并不影响字典的性能。

1.2 底层原理

重写了字典的__missing__方法。当访问字典中不存在的key时,__missing__方法负责返回默认值。因此,defaultdict通过继承dict,重载了__missing__方法,以提供默认值。

1.3 简单使用和解释以及举例

from collections import defaultdict

data_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'])

这里不仅仅可以接受工厂函数 or 类别作为默认值,也可以加入rec= defaultdict(lambda : 0)这种Iterable的函数作为默认值输入。来初始化字典。

2、collections 模块中的 OrderedDict

2.1 OrderedDict 功能

OrderedDict是Python中collections模块中的一种字典,它可以按照元素添加的顺序来存储键值对,保证了元素的顺序性。 与Python中普通字典不同的是,OrderedDict中的元素顺序是有序的,而且支持所有字典的操作。
OrderedDict类的优点是能够维护元素的有序性,缺点是存储元素时需要使用双向链表,因此需要比字典类占用更多的内存空间
字典所拥有的方法 OrderedDict 也同样拥有

2.2 OrderedDict 示例

  • 有序字典,按照元素添加顺序打印键值对
from collections import OrderedDict

d = OrderedDict()
d['python'] = 1
d['java'] = 2
d['go'] = 3
d['php'] = 4

for key, value in d.items():
    print(key, value)

# 打印结果
# python 1
# java 2
# go 3
# php 4
  • 用 OrderedDict 实现先进先出的队列
from collections import OrderedDict


class FIFO(OrderedDict):
    def __init__(self, capacity):
        super().__init__()
        self.capacity = capacity

    def __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)])
  • popitem()
popitem()方法用于弹出最后插入的元素。它接受一个可选的参数last,用于指定弹出的是最后插入的元素还是第一个插入的元素。
from collections import OrderedDict

ordered_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,则移动到链表的头部。
1
from collections import OrderedDict

ordered_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]):从计数器中减去指定的元素或计数器。这个方法会修改计数器本身。

ChainMap

chainMap 属于Python collections 模块下的一个子类,作用是将多个字典,组织成一个字典。当查询时,会按照构造时传入的字典的顺序进行查询。它比使用这些字典创建一个新字典要快。

简单示例

# 导入ChainMap模块
from collections import ChainMap
h1 = {"a": 1, "b": 2, "c": 3}
h2 = {"a": 10, "b": 20, "d": 30}
# 将h1, h2组织成一个字典
h = ChainMap(h1, h2)
# 在第一个字典中查到了"a"这个key,返回该key对应的value
print(h["a"])
# 1
# 在第一个字典中没有查到了"a"这个key,然后在第二个字典的中查到了这个key,返回该key对应的value
print(h["d"])
#30

什么时候使用呢?

在Python中,配置文件通常是字典,系统配置和个人私有配置同时存在时候,可以使用ChainMap来构造出最终配置文件,构造时候个人私有配置在前,系统配置在后。这样,在使用的时候,个人配置就能覆盖系统配置。

常用操作

初始化:chain_map = ChainMap(*maps)

传入若干个字典,用来初始化。例如:chain_map = ChainMap(h1, h2)。这样就将多个字典组合在一起了。如果在初始化的时候,没有传入字典参数,则默认传入一个空字典。底层是将传入字典的引用保存在一个列表中。

查询:查询时,依次查询每个字典,直到一个键被找到,并返回该值。

from collections import ChainMap
h1 = {"a": 1, "b": 2, "c": 3}
h2 = {"a": 10, "b": 20, "d": 30}
h = ChainMap(h1, h2)
# 搜索操作会依次对字典进行搜索,返回第一个值
print(h["a"])
# 1
print(h["d"])
#30

这样的目的是,让后面字典保持不变。例如第一个字典是私人配置,第二个字典是系统配置,操作只会影响个人配置,不会影响系统配置,这对于系统配置来说,是很重要的。

新增:新增键值对只操作第一个字典。

from collections import ChainMap
h1 = {"a": 1, "b": 2, "c": 3}
h2 = {"a": 10, "b": 20, "d": 30}
h = ChainMap(h1, h2)
# 新增操作只会对第一个字典进行操作,因为保存的是引用,因此,本质上是原字典执行的新增操作
h["x"] = 10000
print(h)# 只对第一个字典进行新增
# ChainMap({'a': 1, 'b': 2, 'c': 3, 'x': 10000}, {'a': 10, 'b': 20, 'd': 30})
print(h1)
# {'a': 1, 'b': 2, 'c': 3, 'x': 10000}

这样的目的是,让后面字典保持不变。例如第一个字典是私人配置,第二个字典是系统配置,操作只会影响个人配置,不会影响系统配置,这对于系统配置来说,是很重要的。

更新:更新键值对只会操作第一个字典。

from collections import ChainMap
h1 = {"a": 1, "b": 2, "c": 3}
h2 = {"a": 10, "b": 20, "d": 30}
h = ChainMap(h1, h2)
# 更新操作只会对第一个字典进行操作,因为保存的是引用,因此,本质上是原字典执行的更新操作
h["a"] = 99
print(h)
# ChainMap({'a': 99, 'b': 2, 'c': 3}, {'a': 10, 'b': 20, 'd': 30})
print(h1)
# {'a': 99, 'b': 2, 'c': 3}

这样的目的是,让后面字典保持不变。例如第一个字典是私人配置,第二个字典是系统配置,操作只会影响个人配置,不会影响系统配置,这对于系统配置来说,是很重要的。

删除:删除键值对只会操作第一个字典。如果操作失败,会抛出异常。

from collections import ChainMap
h1 = {"a": 1, "b": 2, "c": 3}
h2 = {"a": 10, "b": 20, "d": 30}
h = ChainMap(h1, h2)
# 删除操作只会对第一个字典进行操作,因为保存的是引用,因此,本质上是原字典执行的删除操作
del h["a"]
print(h)
# ChainMap({'b': 2, 'c': 3}, {'a': 10, 'b': 20, 'd': 30})
print(h1)
# {'b': 2, 'c': 3}
del h["a"]
# 抛出异常,因为只会对第一个字典进行操作,在一个字典中删除不存在的key抛出异常。(不会对后续字典进行操作)

这样的目的是,让后面字典保持不变。例如第一个字典是私人配置,第二个字典是系统配置,操作只会影响个人配置,不会影响系统配置,这对于系统配置来说,是很重要的。

其他属性与操作

maps 属性:底层保存各个字典的列表。可以直接对该属性进行操作,实质是对原字典进行操作

from collections import ChainMap
h1 = {"a": 1, "b": 2, "c": 3}
h2 = {"a": 10, "b": 20, "d": 30}
h = ChainMap(h1, h2)
print(h.maps)
# [{'a': 1, 'b': 2, 'c': 3}, {'a': 10, 'b': 20, 'd': 30}]
# 使用该属性更改字典
h.maps[0]["a"] = 123
# 本质是改变原字典
print(h1)
# {'a': 123, 'b': 2, 'c': 3}     

new_child(m=None, **kwargs)方法:返回一个新的 ChainMap,字典m 位于第一位,其他位于后面,保存的是引用。如果没有指定m,相当于传入了一个空字典。调用 d.new_child() 等价于 ChainMap({}, *d.maps)。如果指定了任何关键字参数kwargs,它会更新所传入的字典。

from collections import ChainMap
h1 = {"a": 1, "b": 2, "c": 3}
h2 = {"a": 10, "b": 20, "d": 30}
h = ChainMap(h1, h2)
# 传入的字典位于最前,传入的kwargs更新传入的字典
hh = h.new_child({"a":-1}, b=10)
# ChainMap({'a': -1, 'b': 10}, {'a': 1, 'b': 2, 'c': 3}, {'a': 10, 'b': 20, 'd': 30})
print(hh)

parents属性:返回一个新的 ChainMap 对象,该对象等价于原对象去除了第一个字典。一个 d.parents 等价于 ChainMap(*d.maps[1:]) 。

from collections import ChainMap
h1 = {"a": 1, "b": 2, "c": 3}
h2 = {"a": 10, "b": 20, "d": 30}
h = ChainMap(h1, h2)
hh = h.parents
print(hh)
# ChainMap({'a': 10, 'b': 20, 'd': 30})
  • 9
    点赞
  • 25
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值