collections提供的高性能数据结构 | 第12期
在阅读一些python第三方库源码的时候,常常发现他们使用了collections这个模块。出于好奇,就把这个模块的功能和用法了解了一下,发现这确实是一个对python本身内建数据结构的一个很好的扩展。在开发python程序时,最常使用的复合结构大概是list、dict和tuple,但是这些内建数据结构有时用起来并不方便。比如在数据结构中常常需要用到“先入先出”的队列和“先入后出”的栈,使用list就不太方便;比如访问一个dcit中不存在的key时会报错,如果要避免停机就需要进行大量的验证;比如使用tuple时忘记了参数位置与参数意义的对应关系,常常会传错参数等等。这些问题也不是不可解决,只是每次解决时的效率太低。如果已经有了封装好的数据结构来解决这些问题,那么为什么不用呢?本文就讨论一下collections模块扩展python内建复合结构的用法。
1. 频数统计Counter
在数据分析时常常需要统计数据出现的频数和频率,这是coolections提供的Counter就可以派上用场。我们先生成一个随机数列表,然后使用Counter来处理。Counter会返回一个对象,然后通过调用这个对象的most_common()函数就会根据频数进行排序,如果只想获取排在前面的若干个可以传入参数。有时,我们期望的数据并不在本次统计的数据中,如果查询这个不存在的数据项程序会不会崩溃呢?答案是不会崩溃,而是返回数值0。这样的处理自然是比较符合逻辑和日常使用习惯的。
import randomfrom collections import Countera = [random.randint(0, 10) for i in range(20)]cnt = Counter(a)print('频数统计:{}'.format(cnt))print('频数排序:{}'.format(cnt.most_common()))print('不存在的数据项统计结果:{}'.format(cnt[11]))Output:
频数统计: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)]不存在的数据项统计结果:02. 默认值词典defaultdict
python内建的dict类型如果遇到不存在的key就会崩溃,但是日常需求有又常常会查询可能不存在的key。这时使用coolections提供的defaultdict才是上策。defaultdict在创建时需要设置一些默认值的类型,如果查询不存在的key,则将这个key的value设置为指定类型的默认值。以下的演示可以表明这一点。
from collections import defaultdict
d = defaultdict(int)d['hello'] = 1d['world'] = 2print('索引不存在的数据项:{}'.format(d['bird']))print('打印词典:{}'.format(d))Output:
索引不存在的数据项:0打印词典:defaultdict(<class 'int'>, {'hello': 1, 'world': 2, 'bird': 0})接下来演示一种复合数据结构作为词典元素类型的情况
d = defaultdict(list)d['hello'] = 1d['world'] = 2print('索引不存在的数据项:{}'.format(d['bird']))print('打印词典:{}'.format(d))Output:
索引不存在的数据项:[]打印词典: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))Output:
从右侧弹出:14从左侧弹出:5当前队列:deque([6, 7, 8, 9, 10, 11, 12, 13], maxlen=10)4. 具名元组namedtuple
python中提供的tuple类型中数据的位置决定了数据的意义,而位置本身是没有什么提示的,所以常常自己开发的代码过一段时间也不知道该如何设置参数。对于这种困扰,collections提供了namedtuple来解决。namedtuple可以用来创建一种类型,在创建时会指定各个位置的参数名。然后使用创建的类型来存储数据,就可以通过参数来对元组数据进行赋值。只要参数名确定,即使传参顺序错乱也不会传错参数,而且参数名也可以作为参数意义的说明。当然,如果记得参数位置与参数意义的对应关系,也可以使用位置参数进行传参。
from collections import namedtupleStudent = namedtuple('Student', 'id name age')s1 = Student(name='hello', age=12, id=1)s2 = Student(2, 'world', 13)print(s1)print(s2)Output:
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 。喜欢阅读英文版的朋友可以跳转到这个网页阅读。
github链接:https://github.com/cnbluegeek/notebook/tree/master
欢迎对python和人工智能感兴趣的朋友加入微信群讨论和交流: