python的collections库

前言

用了这么长时间的python,一直觉得collections库里面的东西真是太好用了,然而发现很多人竟然还没听过,忍不住想安利一下。

简介

collections是python标准库中的一员。官方文档解释为:Container datatypes。提供了对python的基础(容器)数据类型dict、list、set、tuple的替代选择。

模块内成员有:

  • namedtuple # 命名元组
  • deque # 双端队列
  • ChainMap # 链式字典
  • Counter # 计数器
  • OrderedDict # 有序字典
  • defaultdict # 默认值字典
  • UserDict
  • UserList
  • UserString

namedtuple(命名元组)

1
collections.namedtuple(typename, field_names, *, rename=False, defaults=None, module=None)

导入

1
from collections import namedtuple

创建

1
2
3
4
5
Point = namedtuple('Point', ['x', 'y'])
# 文档字符串查看
print(Point.__doc__)
# >>> 'Point(x, y)'
p = Point(11, y=22)

用法

1
2
3
4
5
6
7
8
9
10
11
12
# 类tuple操作
print(p[0] + p[1])
# >>> 33

# 解包
x, y = p
print(x, y)
# >>> (11, 22)

# 属性访问
print(p.x)
# >>> 11

用途

因为namedtuple更节省内存

  1. 代替某些仅需要访问数据的类
    1
    2
    3
    4
    5
    6
    7
    8
    class Stock(object):
    def __init__(self,name,shares,price):
    self.name = name
    self.shares = shares
    self.price = price
    # or
    import collections
    Stock = collections.namedtuple('Stock','name shares price')
  2. 代替不需要改变的字典

原理

1.为啥会节省内存?

TODO

2.如何实现的属性访问?

查看源码,发现其实就是给每个参数做了个别名,实质还是通过索引去访问数据

1
2
3
4
5
6
7
8
9
10
11
12
13
14
print(p._source)
'''
class Point(tuple):
'Point(x, y)'
__slots__ = ()
_fields = ('x', 'y')
def __new__(_cls, x, y):
'Create new instance of Point(x, y)'
return _tuple.__new__(_cls, (x, y))
...
x = _property(_itemgetter(0), doc='Alias for field number 0')
y = _property(_itemgetter(1), doc='Alias for field number 1')

'''

deque(双端队列)

1
class collections.deque([iterable[, maxlen]])

Deque队列是由栈或者queue队列生成的(发音是 “deck”,”double-ended queue”的简称)。Deque 支持线程安全,内存高效添加(append)和弹出(pop),从两端都可以,两个方向的大概开销都是 O(1) 复杂度。

虽然 list 对象也支持类似操作,不过这里优化了定长操作和 pop(0) 和 insert(0, v) 的开销。它们引起 O(n) 内存移动的操作,改变底层数据表达的大小和位置。

如果 maxlen 没有指定或者是 None ,deques 可以增长到任意长度。否则,deque就限定到指定最大长度。一旦限定长度的deque满了,当新项加入时,同样数量的项就从另一端弹出。限定长度deque提供类似Unix filter tail 的功能。它们同样可以用与追踪最近的交换和其他数据池活动。

创建

1
test_deque = collections.deque([1, 2, 3, 4], 1000)

用法

1
2
3
4
5
6
7
8
9
10
11
12
13
14
from collections import deque

test_deque = deque()
test_deque.append(1)
test_deque.append(2)
test_deque.append(3)
test_deque.appendleft(0)
# >>> deque([0, 1, 2, 3])

test_deque.pop()
# >>> deque([0, 1, 2])

test_deque.popleft()
# >>> deque([1, 2])

用途

  1. deque特点在于双端操作速度都快,如果要将数据插入列表头部或者从头部提取数据(保持处理顺序)则使用deque能够提升很大效率
  2. deque支持限制队列长度,在某些类型的应用,比如保留1000条日志时很有用。

性能测试结果

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
28
29
30
31
32
33
34
35
36
37
# coding:utf8
import time
from concurrent.futures import ThreadPoolExecutor
from collections import deque

pool = ThreadPoolExecutor(100)

data_length = 100000

# 测试
def test(_type):
start = time.time()

if _type == "list":
l = []

def work(i):
l.insert(0, i)

elif _type == "deque":
l = deque()

def work(i):
l.appendleft(i)

r = list(pool.map(work, range(data_length)))
print(f"{_type}: {time.time() - start}")
return


test("deque")
test("list")

"""
deque: 3.510190725326538
list: 6.319669008255005
"""

ChainMap

一个 ChainMap 将多个字典或者其他映射组合在一起,创建一个单独的可更新的视图。 如果没有 maps 被指定,就提供一个默认的空字典,这样一个新链至少有一个映射。

创建

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

cm = ChainMap({1: 1}, {2: 2}, {3: 3})

print(cm[1])
# >>> 1
print(cm[2])
# >>> 2

用途

  1. 模拟python内部lookup链
    1
    2
    import builtins
    pylookup = ChainMap(locals(), globals(), vars(builtins))

Counter

一个计数器工具提供快速和方便的计数

创建

1
2
3
from collections import Counter
c = Counter("aaacccddd")
c = Counter(["a", "a", "b", "c", "d"])

用法

  1. 计数
    1
    2
    3
    4
    5
    6
    c = Counter(["a", "a", "b", "c", "d"])
    # >>> Counter({'a': 2, 'b': 1, 'c': 1, 'd': 1})
    c += Counter(["a", "a", "b", "c", "d"])
    # >>> Counter({'a': 4, 'b': 2, 'c': 2, 'd': 2})
    c -= Counter(["a", "a", "b", "c", "d"])
    # >>> Counter({'a': 2, 'b': 1, 'c': 1, 'd': 1})
  2. 统计重复最多的元素
    1
    2
    3
    c = Counter(["a", "a", "b", "c", "d"])
    c.most_common(1)
    # >>> [('a', 4)]

defaultdict

1
class collections.defaultdict([default_factory[, ...]])

返回一个新的类似字典的对象。 defaultdict 是内置 dict 类的子类。它重载了一个方法并添加了一个可写的实例变量

创建

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
from collections import defaultdict

d = defaultdict(int)
print(d["xxx"])
# >>> 0


d = defaultdict(dict)
print(d["xxx"])
# >>> {}

d = defaultdict(lambda: defaultdict(int))
print(d["xxx"]["xxx"])
# >>> 0

d = defaultdict(lambda: defaultdict(lambda: defaultdict(lambda: defaultdict(int))))
print(d["xxx"]["xxx"]["xxx"]["xxx"])
# >>> 0

用法

  1. 提供默认值

    1
    2
    3
    4
    5
    6
    7
    d = defaultdict(int)
    print(d["xxx"])
    # >>> 0

    d = {}
    print(d.get("xxx", 0))
    # >>> 0
  2. 对多层嵌套数据结构提供默认值

    1
    2
    3
    4
    # 预定义数据结构
    d = defaultdict(lambda: {"name": "", "child": defaultdict(str)})

    print(d["xxx"]["child"]["tom"])
  3. 省去if判断

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    d = defaultdict(list)
    d["name"].append("xxx")
    d["age"].append("111")
    print(d)
    # >>> defaultdict(<class 'list'>, {'name': ['xxx'], 'age': ['111']})

    d = {}
    if "name" not in d:
    d["name"] = []
    d["name"].append("xxx")