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超简单解决约瑟夫环问题
May 12 Python
python实现unicode转中文及转换默认编码的方法
Apr 29 Python
Python产生Gnuplot绘图数据的方法
Nov 09 Python
python+opencv打开摄像头,保存视频、拍照功能的实现方法
Jan 08 Python
Python3.5面向对象程序设计之类的继承和多态详解
Apr 24 Python
Python发展简史 Python来历
May 14 Python
解决Django一个表单对应多个按钮的问题
Jul 18 Python
基于python操作ES实例详解
Nov 16 Python
Python高级特性——详解多维数组切片(Slice)
Nov 26 Python
tensorflow实现对张量数据的切片操作方式
Jan 19 Python
python 怎样进行内存管理
Nov 10 Python
python 如何用terminal输入参数
May 25 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多文件上传功能实现原理及代码
2013/04/18 PHP
PHP代码审核的详细介绍
2013/06/13 PHP
destoon在各个服务器下设置URL Rewrite(伪静态)的方法
2014/06/21 Servers
异步加载技术实现当滚动条到最底部的瀑布流效果
2014/09/16 PHP
php判断一个数组是否为有序的方法
2015/03/27 PHP
详解PHP错误日志的获取方法
2015/07/20 PHP
PHP数据库操作二:memcache用法分析
2017/08/16 PHP
PHP调用微博接口实现微博登录的方法示例
2018/09/22 PHP
Laravel 实现Controller向blade前台模板赋值的四种方式小结
2019/10/22 PHP
PHP中isset、empty的用法与区别示例详解
2020/11/05 PHP
CSS+jQuery实现的一个放大缩小动画效果
2013/09/24 Javascript
JQuery中模拟image的ajaxPrefilter与ajaxTransport处理
2015/06/19 Javascript
javascript遇到html5的一些表单属性
2015/07/05 Javascript
js实现仿MSN带关闭功能的右下角弹窗代码
2015/09/04 Javascript
学习JavaScript设计模式之享元模式
2016/01/18 Javascript
基于Vue.js实现数字拼图游戏
2016/08/02 Javascript
JS实现的简单图片切换功能示例【测试可用】
2017/02/14 Javascript
JS中cookie的使用及缺点讲解
2017/05/13 Javascript
Nodejs模块的调用操作实例分析
2018/12/25 NodeJs
微信小程序封装自定义弹窗的实现代码
2019/05/08 Javascript
vue el-table实现自定义表头
2019/12/11 Javascript
区分vue-router的hash和history模式
2020/10/03 Javascript
python中类和实例如何绑定属性与方法示例详解
2017/08/18 Python
浅谈Python实现贪心算法与活动安排问题
2017/12/19 Python
Django 实现下载文件功能的示例
2018/03/06 Python
Python 输出时去掉列表元组外面的方括号与圆括号的方法
2018/12/24 Python
利用pyecharts实现地图可视化的例子
2019/08/12 Python
python 如何引入协程和原理分析
2020/11/30 Python
python中_del_还原数据的方法
2020/12/09 Python
如何编写python的daemon程序
2021/01/07 Python
python lambda的使用详解
2021/02/26 Python
京东港澳售:京东直邮港澳台
2018/01/31 全球购物
Lookfantastic瑞典:英国知名美妆购物网站
2018/04/06 全球购物
科研先进个人典型材料
2014/01/31 职场文书
法院授权委托书范文
2014/08/02 职场文书
2014年终个人总结报告
2015/03/09 职场文书