Python标准库之collections包的使用教程


Posted in Python onApril 27, 2017

前言

Python为我们提供了4种基本的数据结构:list, tuple, dict, set,但是在处理数据量较大的情形的时候,这4种数据结构就明显过于单一了,比如list作为数组在某些情形插入的效率会比较低,有时候我们也需要维护一个有序的dict。所以这个时候我们就要用到Python标准库为我们提供的collections包了,它提供了多个有用的集合类,熟练掌握这些集合类,不仅可以让我们让写出的代码更加Pythonic,也可以提高我们程序的运行效率。

defaultdict

defaultdict(default_factory)在普通的dict之上添加了default_factory,使得key不存在时会自动生成相应类型的value,default_factory参数可以指定成list, set, int等各种合法类型。

我们现在有下面这样一组list,虽然我们有5组数据,但是仔细观察后发现其实我们只有3种color,但是每一种color对应多个值。现在我们想要将这个list转换成一个dict,这个dict的key对应一种color,dict的value设置为一个list存放color对应的多个值。我们可以使用defaultdict(list)来解决这个问题。

>>> from collections import defaultdict
>>> s = [('yellow', 1), ('blue', 2), ('yellow', 3), ('blue', 4), ('red', 1)]
>>> d = defaultdict(list)
>>> for k, v in s:
...  d[k].append(v)
...
>>> sorted(d.items())
[('blue', [2, 4]), ('red', [1]), ('yellow', [1, 3])]

以上等价于:

>>> d = {}
>>> for k, v in s:
...  d.setdefault(k, []).append(v)
...
>>> sorted(d.items())
[('blue', [2, 4]), ('red', [1]), ('yellow', [1, 3])]

如果我们不希望含有重复的元素,可以考虑使用defaultdict(set) 。set相比list的不同之处在于set中不允许存在相同的元素。

>>> from collections import defaultdict
>>> s = [('red', 1), ('blue', 2), ('red', 3), ('blue', 4), ('red', 1), ('blue', 4)]
>>> d = defaultdict(set)
>>> for k, v in s:
...  d[k].add(v)
...
>>> sorted(d.items())
[('blue', {2, 4}), ('red', {1, 3})]

OrderedDict

Python3.6之前的dict是无序的,但是在某些情形我们需要保持dict的有序性,这个时候可以使用OrderedDict,它是dict的一个subclass,但是在dict的基础上保持了dict的有序型,下面我们来看一下使用方法。

>>> # regular unsorted dictionary
>>> d = {'banana': 3, 'apple': 4, 'pear': 1, 'orange': 2}
>>> # dictionary sorted by key
>>> OrderedDict(sorted(d.items(), key=lambda t: t[0]))
OrderedDict([('apple', 4), ('banana', 3), ('orange', 2), ('pear', 1)])
>>> # dictionary sorted by value
>>> OrderedDict(sorted(d.items(), key=lambda t: t[1]))
OrderedDict([('pear', 1), ('orange', 2), ('banana', 3), ('apple', 4)])
>>> # dictionary sorted by length of the key string
>>> OrderedDict(sorted(d.items(), key=lambda t: len(t[0])))
OrderedDict([('pear', 1), ('apple', 4), ('orange', 2), ('banana', 3)])

使用popitem(last=True)方法可以让我们按照LIFO(先进后出)的顺序删除dict中的key-value,即删除最后一个插入的键值对,如果last=False就按照FIFO(先进先出)删除dict中key-value。

>>> d = {'banana': 3, 'apple': 4, 'pear': 1, 'orange': 2}
>>> # dictionary sorted by key
>>> d = OrderedDict(sorted(d.items(), key=lambda t: t[0]))
>>> d
OrderedDict([('apple', 4), ('banana', 3), ('orange', 2), ('pear', 1)])
>>> d.popitem()
('pear', 1)
>>> d.popitem(last=False)
('apple', 4)

使用move_to_end(key, last=True)来改变有序的OrderedDict对象的key-value顺序,通过这个方法我们可以将排序好的OrderedDict对象中的任意一个key-value插入到字典的开头或者结尾。

>>> d = OrderedDict.fromkeys('abcde')
>>> d
OrderedDict([('a', None), ('b', None), ('c', None), ('d', None), ('e', None)])
>>> d.move_to_end('b')
>>> d
OrderedDict([('a', None), ('c', None), ('d', None), ('e', None), ('b', None)])
>>> ''.join(d.keys())
'acdeb'
>>> d.move_to_end('b', last=False)
>>> ''.join(d.keys())
'bacde'

deque

list存储数据的优势在于按索引查找元素会很快,但是插入和删除元素就很慢了,因为list是基于数组实现的。deque是为了高效实现插入和删除操作的双向列表,适合用于队列和栈,而且线程安全。

list只提供了append和pop方法来从list的尾部插入/删除元素,deque新增了appendleft/popleft等方法允许我们高效的在元素的开头来插入/删除元素。而且使用deque在队列两端append或pop元素的算法复杂度大约是O(1),但是对于list对象改变列表长度和数据位置的操作例如 pop(0)insert(0, v)操作的复杂度高达O(n)。

>>> from collections import deque
>>> dq = deque(range(10), maxlen=10)
>>> dq
deque([0, 1, 2, 3, 4, 5, 6, 7, 8, 9], maxlen=10)
>>> dq.rotate(3)
>>> dq
deque([7, 8, 9, 0, 1, 2, 3, 4, 5, 6], maxlen=10)
>>> dq.rotate(-4)
>>> dq
deque([1, 2, 3, 4, 5, 6, 7, 8, 9, 0], maxlen=10)
>>> dq.appendleft(-1)
>>> dq
deque([-1, 1, 2, 3, 4, 5, 6, 7, 8, 9], maxlen=10)
>>> dq.extend([11, 22, 33])
>>> dq
deque([3, 4, 5, 6, 7, 8, 9, 11, 22, 33], maxlen=10)
>>> dq.extendleft([10, 20, 30, 40])
>>> dq
deque([40, 30, 20, 10, 3, 4, 5, 6, 7, 8], maxlen=10)

Counter

Count用来统计相关元素的出现次数。

>>> from collections import Counter
>>> ct = Counter('abracadabra')
>>> ct
Counter({'a': 5, 'r': 2, 'b': 2, 'd': 1, 'c': 1})
>>> ct.update('aaaaazzz')
>>> ct
Counter({'a': 10, 'z': 3, 'r': 2, 'b': 2, 'd': 1, 'c': 1})
>>> ct.most_common(2)
[('a', 10), ('z', 3)]
>>> ct.elements()
<itertools.chain object at 0x7fbaad4b44e0>

namedtuple

使用namedtuple(typename, field_names)命名tuple中的元素来使程序更具可读性。

>>> from collections import namedtuple
>>> City = namedtuple('City', 'name country population coordinates')
>>> tokyo = City('Tokyo', 'JP', 36.933, (35.689722, 139.691667))
>>> tokyo
City(name='Tokyo', country='JP', population=36.933, coordinates=(35.689722, 139.691667))
>>> tokyo.population
36.933
>>> tokyo.coordinates
(35.689722, 139.691667)
>>> tokyo[1]
'JP'
>>> City._fields
('name', 'country', 'population', 'coordinates')
>>> LatLong = namedtuple('LatLong', 'lat long')
>>> delhi_data = ('Delhi NCR', 'IN', 21.935, LatLong(28.613889, 77.208889))
>>> delhi = City._make(delhi_data)
>>> delhi._asdict()
OrderedDict([('name', 'Delhi NCR'), ('country', 'IN'), ('population', 21.935),
   ('coordinates', LatLong(lat=28.613889, long=77.208889))])
>>> for key, value in delhi._asdict().items():
  print(key + ':', value)
name: Delhi NCR
country: IN
population: 21.935
coordinates: LatLong(lat=28.613889, long=77.208889)

ChainMap

ChainMap可以用来合并多个字典。

>>> from collections import ChainMap
>>> d = ChainMap({'zebra': 'black'}, {'elephant': 'blue'}, {'lion': 'yellow'})
>>> d['lion'] = 'orange'
>>> d['snake'] = 'red'
>>> d
ChainMap({'lion': 'orange', 'zebra': 'black', 'snake': 'red'},
   {'elephant': 'blue'}, {'lion': 'yellow'})
>>> del d['lion']
>>> del d['elephant']
Traceback (most recent call last):
 File "/usr/lib/python3.5/collections/__init__.py", line 929, in __delitem__
 del self.maps[0][key]
KeyError: 'elephant'
During handling of the above exception, another exception occurred:
Traceback (most recent call last):
 File "<stdin>", line 1, in <module>
 File "/usr/lib/python3.5/collections/__init__.py", line 931, in __delitem__
 raise KeyError('Key not found in the first mapping: {!r}'.format(key))
KeyError: "Key not found in the first mapping: 'elephant'"

从上面del['elephant']的报错信息可以看出来,对于改变键值的操作ChainMap只会在第一个字典self.maps[0][key]进行查找,新增加的键值对也都会加入第一个字典,我们来改进一下ChainMap解决这个问题:

class DeepChainMap(ChainMap):
 'Variant of ChainMap that allows direct updates to inner scopes'
 def __setitem__(self, key, value):
  for mapping in self.maps:
   if key in mapping:
    mapping[key] = value
    return
  self.maps[0][key] = value
 def __delitem__(self, key):
  for mapping in self.maps:
   if key in mapping:
    del mapping[key]
    return
  raise KeyError(key)
>>> d = DeepChainMap({'zebra': 'black'}, {'elephant': 'blue'}, {'lion': 'yellow'})
>>> d['lion'] = 'orange'   # update an existing key two levels down
>>> d['snake'] = 'red'   # new keys get added to the topmost dict
>>> del d['elephant']   # remove an existing key one level down
DeepChainMap({'zebra': 'black', 'snake': 'red'}, {}, {'lion': 'orange'})

可以使用new_child来deepcopy一个ChainMap:

>>> from collections import ChainMap
>>> a = {'a': 'A', 'c': 'C'}
>>> b = {'b': 'B', 'c': 'D'}
>>> m = ChainMap({'a': 'A', 'c': 'C'}, {'b': 'B', 'c': 'D'})
>>> m
ChainMap({'a': 'A', 'c': 'C'}, {'b': 'B', 'c': 'D'})
>>> m['c']
'C'
>>> m.maps
[{'c': 'C', 'a': 'A'}, {'c': 'D', 'b': 'B'}]
>>> a['c'] = 'E'
>>> m['c']
'E'
>>> m
ChainMap({'c': 'E', 'a': 'A'}, {'c': 'D', 'b': 'B'})
>>> m2 = m.new_child()
>>> m2['c'] = 'f'
>>> m2
ChainMap({'c': 'f'}, {'c': 'E', 'a': 'A'}, {'c': 'D', 'b': 'B'})
>>> m
ChainMap({'c': 'E', 'a': 'A'}, {'c': 'D', 'b': 'B'})
>>> m2.parents
ChainMap({'c': 'E', 'a': 'A'}, {'c': 'D', 'b': 'B'})

UserDict

下面我们来改进一下字典,查询字典的时候将key转换为str的形式:

class StrKeyDict0(dict):
 def __missing__(self, key):
  if isinstance(key, str):
   raise KeyError(key)
  return self[str(key)]
 def get(self, key, default=None):
  try:
   return self[key]
  except KeyError:
   return default
 def __contains__(self, key):
  return key in self.keys() or str(key) in self.keys()

解释一下上面这段程序:

  • 在__missing__中isinstance(key, str)是必须要的,请思考一下为什么? 因为假设一个key不存在的话,这会造成infinite recursion,self[str(key)]会再次调用__getitem__。
  • __contains__也是必须实现的,因为k in d的时候会进行调用,但是注意即使查找失败它也不会调用__missing__。关于__contains__还有一个细节就是:我们并没有使用k in my_dict,因为str(key) in self的形式,因为这会造成递归调用__contains__。

这里还强调一点,在Python2.x中dict.keys()会返回一个list,这意味着k in my_list必须遍历list。在Python3.x中针对dict.keys()做了优化,性能更高,它会返回一个view如同set一样,详情参考官方文档。

上面这个例子可以用UserDict改写,并且将所有的key都以str的形式存储,而且这种写法更加常用简洁:

import collections
class StrKeyDict(collections.UserDict):
 def __missing__(self, key):
  if isinstance(key, str):
   raise KeyError(key)
  return self[str(key)]
 def __contains__(self, key):
  return str(key) in self.data
 def __setitem__(self, key, item):
  self.data[str(key)] = item

UserDict是MutableMapping和Mapping的子类,它继承了MutableMapping.update和Mapping.get两个重要的方法,所以上面我们并没有重写get方法,可以在源码中看到它的实现和我们上面的实现是差不多的。

总结

以上就是这篇文章的全部内容了,希望本文的内容对大家学习或者工作能带来一定的帮助,如果有疑问大家可以留言交流,谢谢大家对三水点靠木的支持。

Python 相关文章推荐
python调用短信猫控件实现发短信功能实例
Jul 04 Python
python获取当前日期和时间的方法
Apr 30 Python
Python实现图片尺寸缩放脚本
Mar 10 Python
Python实现字典排序、按照list中字典的某个key排序的方法示例
Dec 18 Python
Python设计模式之装饰模式实例详解
Jan 21 Python
详解python配置虚拟环境
Apr 08 Python
Pytorch反向求导更新网络参数的方法
Aug 17 Python
python连接PostgreSQL数据库的过程详解
Sep 18 Python
Python Des加密解密如何实现软件注册码机器码
Jan 08 Python
Pycharm pyuic5实现将ui文件转为py文件,让UI界面成功显示
Apr 08 Python
python如何求圆的面积
Jul 01 Python
解决pip安装的第三方包在PyCharm无法导入的问题
Oct 15 Python
Golang与python线程详解及简单实例
Apr 27 #Python
Mac中升级Python2.7到Python3.5步骤详解
Apr 27 #Python
详解Python 2.6 升级至 Python 2.7 的实践心得
Apr 27 #Python
Python中创建字典的几种方法总结(推荐)
Apr 27 #Python
Python中index()和seek()的用法(详解)
Apr 27 #Python
Python中几种导入模块的方式总结
Apr 27 #Python
Python利用matplotlib生成图片背景及图例透明的效果
Apr 27 #Python
You might like
PHP函数引用返回的实例详解
2016/09/11 PHP
JavaScript中的Window窗口对象
2008/01/16 Javascript
js post方式传递提交的实现代码
2010/05/31 Javascript
JavaScript动态添加列的方法
2015/03/25 Javascript
Windows下用PyCharm和Visual Studio开始Python编程
2015/10/26 Javascript
js跨浏览器的事件侦听器和事件对象的使用方法
2015/12/17 Javascript
javascript中不易分清的slice,splice和split三个函数
2016/03/29 Javascript
如何高效率去掉js数组中的重复项
2016/04/12 Javascript
非常棒的jQuery图片轮播效果
2016/04/17 Javascript
WEB前端开发框架Bootstrap3 VS Foundation5
2016/05/16 Javascript
浅谈JSON.stringify()和JOSN.parse()方法的不同
2016/08/29 Javascript
AngularJS动态绑定HTML的方法分析
2016/11/07 Javascript
Javascript 实现匿名递归的实例代码
2017/05/25 Javascript
Vue.js弹出模态框组件开发的示例代码
2017/07/26 Javascript
vue 本地环境跨域请求proxyTable的方法
2018/09/19 Javascript
JavaScript页面倒计时功能完整示例
2019/05/15 Javascript
express框架下使用session的方法
2019/07/31 Javascript
原生JS实现微信通讯录
2020/06/18 Javascript
详解在Python程序中使用Cookie的教程
2015/04/30 Python
在Django中管理Users和Permissions以及Groups的方法
2015/07/23 Python
读取json格式为DataFrame(可转为.csv)的实例讲解
2018/06/05 Python
python操作excel的方法
2018/08/16 Python
python 将json数据提取转化为txt的方法
2018/10/26 Python
python版大富翁源代码分享
2018/11/19 Python
python里dict变成list实例方法
2019/06/26 Python
基于numpy中的expand_dims函数用法
2019/12/18 Python
python GUI库图形界面开发之PyQt5切换按钮控件QPushButton详细使用方法与实例
2020/02/28 Python
python切片作为占位符使用实例讲解
2021/02/17 Python
探索HTML5本地存储功能运用技巧
2016/03/02 HTML / CSS
中文系学生自荐信范文
2013/11/13 职场文书
个人能力自我鉴赏
2014/01/25 职场文书
政府信息公开实施方案
2014/05/09 职场文书
四十年同学聚会致辞
2015/07/28 职场文书
SpringBoot2 参数管理实践之入参出参与校验的方式
2021/06/16 Java/Android
MySQL分区表管理命令汇总
2022/03/21 MySQL
CentOS7和8下安装Maven3.8.4
2022/04/07 Servers