Python 原生数据结构增强模块 collections
collections 简介
python 提供了 4 种基本的数据结构:list、tuple、dict、set。基本数据结构完全可以 hold 住所有的场景,但是在处理数据结构复杂的场景时,这 4 种数据结构有时会显的单一,比如将相同字母组成的字符串归类到列表中,是一个 key 为字符串,value 为列表的数据结构,复杂度为 O(1)的情况下完成 LRU(力扣原题)。
这个时候今天的主角 collections
包就可以登场了。collections 是基本数据结构的高性能优化版,它提供了多个有用的集合类,熟练掌握这些集合类,不仅可以让我们让写出的代码更加 Pythonic,也可以提高我们程序的运行效率。
Counter
简单使用
Counter 是一个简单的计数器,例如,统计字符出现的个数,并且会根据出现次数从大到小排列好:
功能:
Counter 是一个计数器工具,提供快速和方便的计数。从类型上划分,Counter 是一个 dict 的子类,元素像字典一样存储,key 是统计的元素,value 是统计的个数。
Counter 可统计多种数据类型,包括:字符串,列表,字典,元组,集合等。
字符串
列表
字典
元组
集合
Counter 拥有的方法
1.elements()
返回一个迭代器,其中每个元素将重复出现计数值所指定次。元素返回的顺序是按照元素在原本序列中首次出现的顺序
2.most_common()
返回一个列表,其中包含 n 个出现次数最高的元素及出现次数,按常见程度由高到低排序。 如果 n 被省略或为 None, 将返回计数器中的所有元素。
计数值相等的元素按首次出现的顺序排序,经常用来计算 top 词频的词语。
3.subtract()
将两个 Counter 相减,a.sbutract(b),a 中计数减少相应的个数
4.字典方法
通常字典方法都可用于 Counter 对象,除了有两个方法工作方式与字典并不相同。
a. fromkeys(iterable)
这个类方法没有在 Counter 中实现。
b. update([iterable-or-mapping])
Counter 对象更新另一个对象。但是不同之处在于字典的 update 是更新元素的 value,而 Counter 的 update 是将元素的统计相加。
defaultdict
defaultdict 是带默认值的字典,属于内置字典的子集,它重写了一个方法和添加一个可写实例变量。普通字典在取出一个不存在的 key 时,会抛出 KeyError 错误。如果希望在 key 不存在时返回一个默认值,可以使用字典的 get 方法,如 map.get(key, None),或者是使用 defaultdict。defaultdict 作用就是在字典查找不存在的 key 时返回一个默认值而不是 KeyError。
功能:
为添加到字典中的每一个 key 增加一个默认值,当查询一个不存在的元素时,会返回该默认类型和添加一个可写实例变量。剩下的方法都和常规字典一样。
默认值为列表
默认值为数字
默认值的类型
当访问不存在的 key 时,会返回默认值,同时在字典中创建该记录。
deque
在数据结构中,队列和堆栈是两个非常重要的数据类型,一个先进先出,一个后进先出。在 python 中,使用 list 存储数据时,按索引访问元素很快,但是插入和删除元素就很慢了,因为 list 是线性存储,数据量大的时候,插入和删除效率很低。
deque 是为了高效实现插入和删除操作的双向链表结构,非常适合实现队列和堆栈这样的数据结构
功能:
deque 是一个栈和队列的综合,发音为 deck,可以成为双向队列。deque 支持线程安全,两端都支持高效的入栈和出栈操作,时间复杂度为 O(1)。
虽然 list 支持类似的操作,但它们针对快速的固定长度操作进行了优化,并为 pop(0) 和 insert(0,v)操作带来了 O(n)内存移动成本,这两种操作都会更改基础数据表示的大小和位置。
如果未指定 maxlen 或为 None,则 deque 可能会增长到任意长度。否则,deque 将绑定到指定的最大长度。一旦有界长度 deque 已满,添加新项时,从另一端丢弃相应数量的项。
deque 拥有的方法
deque 拥有众多的方法,因为 deque
是一个双向链表,所以在头和尾都可以操作,方法有如下:
deque.append(x)
在队列右边(尾部)插入 x
deque.appendleft(x)
在队列左边(头部)插入 x
deque.pop()
在队列左边(尾部)出栈
deque.popleft()
在队列右边(头部)出栈
deque.clear()
清空队列,初始化队列长度为 1
deque.copy()
创建一个浅拷贝的队列
deque.count(x)
计算队列中 x 元素的个数
deque.extend(iterable)
将可迭代对象扩展到队列右边
deque.extendleft(iterable)
将可迭代对象扩展到队列左边
deque.index(x)
返回元素 x 在队列中的位置,没有找到抛出异常 ValueError
deque.insert(i, x)
插入元素 x 到指定位置 x。如果位置超出范围,抛出 IndexError 异常
deque.remove(x)
删除第一个找到的元素 x,没有元素抛出 ValueError 异常
deque.reverse()
逆序
deque.rotate(n)
将队列向右移动 n 个元素,如果 n 为负数则向左移动 n 个元素
deque.maxlen()
返回队列长度,为空返回 None。
namedtuple
命名元组类似于一个对象,包含只读属性的变量
功能:
命名元组为元组中的每个位置分配意义,并允许更可读、自文档化的代码。它们可以在任何使用常规元组的地方使用,并且它们增加了通过名称而不是位置索引访问字段的能力。
namedtuple 创建一个和 tuple 类似的对象,而且对象可以通过名字访问。这对象更像带有数据属性的类,不过数据属性是只读的。
1)typename:该参数指定所创建的命名元组的名字
2) field_names:该参数指定各个元素对应的名字,如 ['x','y'],也可以用"x,y"
简单使用
有一个长方体,长宽高的属性用一个元组来表示(12, 15, 16)。虽然三个数值能够表示一个长方体,但是这个元组并没有清楚的指明三个字段分别代表什么。其实三个字段分别是:
使用 namedtuple 来表示就能够很清晰的表示出来。
可以看出 namedtuple 的方法方式有如下关系:
noe.length
== one[0]
one.width
== one[1]
one.height
== one[2]
三个方法
除了继承元组的方法,命名元组还支持三个额外的方法和两个属性
1._asdict()
返回一个新的 dict ,它将字段名称映射到它们对应的值
2._replace
3._make
类方法,从存在的序列或迭代实例创建一个新实例。
两个属性
1._fields
出了字段名
2.defaults
给字段设置默认值
OrderedDict
有顺序的字典和常规字典类似,但是有很多和排序有关的能力。但是有序字典现在变得不是那么重要了,因为在 python3.7 开始常规字典也能记住插入的顺序。
有序字典和常规字典仍然有一些不同之处:
Python3.6 之前的 dict 是无序的,但是在某些情形我们需要保持 dict 的有序性,这个时候可以使用 OrderedDict.
两个特殊方法
1. popitem
删除头或尾
dic.popitem(): 删除尾元素
dic.popitem(False): 删除头元素
2. dic.move_to_end()
: 将指定键值对移动到尾部
ChainMap
ChainMap 映射链
功能:
ChainMap 提供了一种多个字典整合的方式,它没有去合并这些字典,而是将这些字典放在一个列表(maps)里,内部实现了很多 dict 的方法,大部分 dict 的方法,ChainMap 都能使用。
在存储中类似于[a,b],即[{'x': 1, 'y': 2}, {'x': 100, 'z': 200}],这是 ChainMap 的特点,按照前后顺序存储每一个 map
操作
1.读取
对 ChainMap 的读取会从第一个 map 开始读,直到遇到指定的 key,返回第一个遇到的元素
2.更新
对 ChainMap 的修改只会影响第一个元素
拥有的方法
1.maps
一个可以更新的映射列表。这个列表是按照第一次搜索到最后一次搜索的顺序组织的。它是仅有的存储状态,可以被修改。列表最少包含一个映射。
ChainMap 内部存储了一个名为 maps 的 list 用以维护 mapping 关系, 这个 list 可以直接查看和修改,修改之后相应 ChainMap 值也就修改了.
2.new_child
new_child(m=None, **kwargs)
返回一个新的 ChainMap,其中包含一个新的映射,后面跟随当前实例中的所有映射。 如果指定了 m,它会成为新的映射加在映射列表的前面;如果未指定,则会使用一个空字典,因此调用 d.new_child() 就等价于 ChainMap({}, *d.maps)。 如果指定了任何关键字参数,它们会更新所传入的映射或新的空字典。 此方法被用于创建子上下文,它可在不改变任何上级映射的情况下被更新。
3.parents
属性返回一个新的 ChainMap 包含所有的当前实例的映射,除了第一个。这样可以在搜索的时候跳过第一个映射。 使用的场景类似在 nested scopes 嵌套作用域中使用 nonlocal 关键词。用例也可以类比内建函数 super() 。
一个 d.parents 的引用等价于 ChainMap(*d.maps[1:]) 。
ChainMap 只是对之前的字典做一个引用,因此,修改 ChainMap 会影响到之前的字典,同理修改原来的字典也会影响到 ChainMap
总结
collections 号称是基本数据结构的增强版,如果基本数据结构是 iphone13,那 collections 就是 iphone13 pro,iphone13 pro max。可以将是否熟练使用 collections 作为 python 进阶编程的一个衡量标准。
版权声明: 本文为 InfoQ 作者【Java全栈架构师】的原创文章。
原文链接:【http://xie.infoq.cn/article/a4aa67e8ea7957c9694e96584】。未经作者许可,禁止转载。
评论