这里有新鲜出炉的 Python 多线程编程,程序狗速度看过来!
Python 是一种面向对象、解释型计算机程序设计语言,由 Guido van Rossum 于 1989 年底发明,第一个公开发行版发行于 1991 年。Python 语法简洁而清晰,具有丰富和强大的类库。它常被昵称为胶水语言,它能够把用其他语言制作的各种模块(尤其是 C/C++)很轻松地联结在一起。
这篇文章主要给大家介绍了 Python 标准库之 collections 包的使用教程,详细介绍了 collections 中多个集合类的使用方法,相信对大家具有一定的参考价值,需要的朋友们下面随小编一起来学习学习吧。
前言
Python 为我们提供了 4 种基本的数据结构:list, tuple, dict, set,但是在处理数据量较大的情形的时候,这 4 种数据结构就明显过于单一了,比如 list 作为数组在某些情形插入的效率会比较低,有时候我们也需要维护一个有序的 dict。所以这个时候我们就要用到 Python 标准库为我们提供的 collections 包了,它提供了多个有用的集合类,熟练掌握这些集合类,不仅可以让我们让写出的代码更加 Pythonic,也可以提高我们程序的运行效率。
defaultdict
在普通的 dict 之上添加了 default_factory,使得 key 不存在时会自动生成相应类型的 value,default_factory 参数可以指定成 list, set, int 等各种合法类型。
- defaultdict(default_factory)
我们现在有下面这样一组 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])]
如果我们不希望含有重复的元素,可以考虑使用
。set 相比 list 的不同之处在于 set 中不允许存在相同的元素。
- defaultdict(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)])
使用
方法可以让我们按照 LIFO(先进后出) 的顺序删除 dict 中的 key-value,即删除最后一个插入的键值对,如果 last=False 就按照 FIFO(先进先出) 删除 dict 中 key-value。
- popitem(last=True)
- >>> 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)
使用
来改变有序的 OrderedDict 对象的 key-value 顺序,通过这个方法我们可以将排序好的 OrderedDict 对象中的任意一个 key-value 插入到字典的开头或者结尾。
- move_to_end(key, last=True)
- >>> 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)
操作的复杂度高达 O(n)。
- insert(0, v)
- >>> 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
使用
命名 tuple 中的元素来使程序更具可读性。
- namedtuple(typename, field_names)
- >>> 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'"
从上面
的报错信息可以看出来,对于改变键值的操作 ChainMap 只会在第一个字典
- del['elephant']
进行查找,新增加的键值对也都会加入第一个字典,我们来改进一下 ChainMap 解决这个问题:
- self.maps[0][key]
- 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()
解释一下上面这段程序:
,因为
- k in my_dict
的形式,因为这会造成递归调用__contains__。
- str(key) in self
这里还强调一点,在 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 方法,可以在源码中看到它的实现和我们上面的实现是差不多的。
总结
来源: http://www.phperz.com/article/17/0515/334864.html