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 factory
    
    The default factory is called without arguments to produce
    a 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 were
    passed to the dict constructor, including keyword arguments.
    """
    def copy(self): # real signature unknown; restored from __doc__
        """ D.copy() -> a shallow copy of D. """
        pass

    def __copy__(self, *args, **kwargs): # real signature unknown
        """ D.copy() -> a shallow copy of D. """
        pass

    def __getattribute__(self, *args, **kwargs): # real signature unknown
        """ Return getattr(self, name). """
        pass

    def __init__(self, default_factory=None, **kwargs): # known case of _collections.defaultdict.__init__
        """
        defaultdict(default_factory[, ...]) --> dict with default factory
        
        The default factory is called without arguments to produce
        a 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 were
        passed to the dict constructor, including keyword arguments.
        
        # (copied from class doc)
        """
        pass

    def __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
        """
        pass

    def __reduce__(self, *args, **kwargs): # real signature unknown
        """ Return state information for pickling. """
        pass

    def __repr__(self, *args, **kwargs): # real signature unknown
        """ Return repr(self). """
        pass

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

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 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)])
定义了一个FIFO类,该类继承了OrderedDict,并重载了__setitem__方法,实现了一个先进先出的队列。
当队列已满时,每次添加新元素时,会删除最早添加的元素,从而使队列长度保持不变。
在代码中,当队列长度已经达到最大长度时,通过调用self.popitem(last=False)方法删除最早添加的元素,
即队首元素,从而腾出位置来添加新元素。
  • 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,则移动到链表的头部。
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]):从计数器中减去指定的元素或计数器。这个方法会修改计数器本身。
from collections import Counter

c = 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 python

from collections import Counter

with 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)]

持续更新中。。。

  • 3
    点赞
  • 47
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 0
    评论

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

还是那个同伟伟

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值