collections提供的高性能数据结构

BG34

Posted by Blue Geek on October 18, 2019

collections提供的高性能数据结构

By 青衣极客 Blue Geek In 2019-10-18

在阅读一些python第三方库源码的时候,常常发现他们使用了collections这个模块。出于好奇,就把这个模块的功能和用法了解了一下,发现这确实是一个对python本身内建数据结构的一个很好的扩展。在开发python程序时,最长使用的复合结构大概是list、dict和tuple,但是这些内建数据结构有时用起来并不方便。比如在数据结构中常常需要用到“先入先出”的队列和“先入后出”的栈,使用list就不太方便;比如访问一个dcit中不存在的key时会报错,如果要避免停机就需要进行大量的验证;比如使用tuple时忘记了参数位置与参数意义的对应关系,常常会传错参数等等。这些问题也不是不可解决,只是每次解决时的效率太低。如果已经有了封装好的数据结构来解决这些问题,那么为什么不用呢?本文就讨论一下collections模块扩展python内建复合结构的用法。

1. 频数统计Counter

在数据分析时常常需要统计数据出现的频数和频率,这是coolections提供的Counter就可以派上用场。我们先生成一个随机数列表,然后使用Counter来处理。Counter会返回一个对象,然后通过调用这个对象的most_common()函数就会根据频数进行排序,如果只想获取排在前面的若干个可以传入参数。有时,我们期望的数据并不在本次统计的数据中,如果查询这个不存在的数据项程序会不会崩溃呢?答案是不会崩溃,而是返回数值0。这样的处理自然是比较复合逻辑和日常使用习惯的。

import random
from collections import Counter
a = [random.randint(0, 10) for i in range(20)]
cnt = Counter(a)
print('频数统计:{}'.format(cnt))
print('频数排序:{}'.format(cnt.most_common()))
print('不存在的数据项统计结果:{}'.format(cnt[11]))
频数统计:Counter({2: 4, 5: 4, 0: 2, 8: 2, 4: 2, 7: 2, 1: 2, 3: 1, 10: 1})
频数排序:[(2, 4), (5, 4), (0, 2), (8, 2), (4, 2), (7, 2), (1, 2), (3, 1), (10, 1)]
不存在的数据项统计结果:0

2. 默认值词典defaultdict

python内建的dict类型如果遇到不存在的key就会崩溃,但是日常需求有又常常会查询可能不存在的key。这是使用coolections提供的defaultdict才是上策。defaultdict在创建时需要设置一些默认值的类型,如果查询不存在的key,则将这个key的value设置为指定类型的默认值。以下的演示可以表明这一点。

from collections import defaultdict

d = defaultdict(int)
d['hello'] = 1
d['world'] = 2
print('索引不存在的数据项:{}'.format(d['bird']))
print('打印词典:{}'.format(d))
索引不存在的数据项:0
打印词典:defaultdict(<class 'int'>, {'hello': 1, 'world': 2, 'bird': 0})
d = defaultdict(list)
d['hello'] = 1
d['world'] = 2
print('索引不存在的数据项:{}'.format(d['bird']))
print('打印词典:{}'.format(d))
索引不存在的数据项:[]
打印词典:defaultdict(<class 'list'>, {'hello': 1, 'world': 2, 'bird': []})

3. 队列deque

在实现一些逻辑时具有“先入先出”或者“先入后出”这样预定义规则的数据结构时会方便很多。这种数据结构常常用作缓冲区,那么就需要指定缓冲区的大小。collections提供了deque类型,这个类型的数据定义压入和弹出操作,正好可以用来做队列或者栈。以下演示中append()函数压入元素,popleft()表示从左侧弹出,也可以理解为底部弹出,pop()表示从右侧弹出,也可以理解为顶部弹出。所以使用popleft则相当与队列,使用pop就相当于栈。

from collections import deque

q = deque(maxlen=10)
for i in range(15):
    q.append(i)
print('从右侧弹出:{}'.format(q.pop()))
print('从左侧弹出:{}'.format(q.popleft()))
print('当前队列:{}'.format(q))
从右侧弹出:14
从左侧弹出:5
当前队列:deque([6, 7, 8, 9, 10, 11, 12, 13], maxlen=10)

4. 具名元组namedtuple

python中提供的tuple类型中数据的位置决定了数据的意义,而位置本身是没有什么提示的,所以常常自己开发的代码过一段时间也不知道该如何设置参数。对于这种困扰,collections提供了namedtuple来解决。namedtuple可以用来创建一种类型,在创建时会指定各个位置的参数名。然后使用创建的类型来存储数据,就可以通过参数来对元组数据进行赋值。只要参数名确定,即使传参顺序错乱也不会传错参数,而且参数名也可以作为参数意义的说明。当然,如果记得参数位置与参数意义的对应关系,也可以使用位置参数进行传参。

from collections import namedtuple
Student = namedtuple('Student', 'id name age')
s1 = Student(name='hello', age=12, id=1)
s2 = Student(2, 'world', 13)
print(s1)
print(s2)

Student(id=1, name='hello', age=12)
Student(id=2, name='world', age=13)

到此,使用collections模块对python内建复合结构进行扩展就讨论到这里,本文在写作时参考的英文版网页如下:https://levelup.gitconnected.com/introducing-high-performance-datatypes-in-python-with-the-collections-library-3d8c334827a5 。喜欢阅读英文版的朋友可以跳转到这个网页阅读。

【青衣极客】公众号



COMMENT

博客评论区功能由Github Issue提供,提交Issue时请以本文标题为话题

"BG34-collections提供的高性能数据结构"