Python的collections模块

collections —- 容器数据类型

Python3 collections模块使用详解

  1. collections — Container datatypes
    namedtuple(): 生成可以使用名字来访问元素内容的tuple子类
    deque: 双端队列,可以快速的从另外一侧追加和推出对象
    Counter: 计数器,主要用来计数
    OrderedDict: 有序字典
    defaultdict: 带有默认值的字典
           ChainMap    类似字典(dict)的容器类,将多个映射集合到一个视图里面
           UserDict    封装了字典对象,简化了字典子类化
           UserList    封装了列表对象,简化了列表子类化
           UserString    封装了列表对象,简化了字符串子类化
    
    collections.abc 抽象基类

tuple的几个特性:

  1. 不可变,iterable
  2. 拆包
  3. tuple不可变不是绝对的
  4. tuple比list好的地方
    4.1 immutable的重要性:性能优化(元素全为immutable的会作为常量在编译时确定)、线程安全、可以作为dict的key,拆包特性
    4.2 tuple类似struct,list类似array

容器序列

  listtuplecollections.deque 这些序列能存放不同类型的数据。

扁平序列

  strbytesbytearraymemoryviewarray.array,这类序列只能容纳一种类型。

容器序列存放的是它们所包含的任意类型的对象的引用,而扁平序列里存放的是值而不是引用。换句话说,扁平序列其实是一段连续的内存空间。由此可见扁平序列其实更加紧凑,但是它里面只能存放诸如字符、字节和数值这种基础类型。

序列类型还能按照能否被修改来分类。

可变序列

  listbytearrayarray.arraycollections.dequememoryview

不可变序列

  tuplestrbytes

具名元组namedtuple

概念

命名的元组,意味给元组中的每个位置赋予含义,意味着代码可读性更强,namedtuple可以在任何常规元素使用的地方使用,而且它可以通过名称来获取字段信息而不仅仅是通过位置索引。用以构建只有少数属性但是没有方法的对象,比如数据库条目。

collections.namedtuple 是一个工厂函数,它可以用来构建一个带字段名的元组和一个有名字的

namedtuple 构建的类的实例所消耗的内存跟元组是一样的,因为字段名都被存在对应的类里面

创建一个具名元组需要两个参数,一个是类名,另一个是类的各个字段的名字。

几个最有用的:_fields 类属性、类方法 _make(iterable) 和实例方法 _asdict()

_fields 属性是一个包含这个类所有字段名称的元组。

❷ 用 _make() 通过接受一个可迭代对象来生成这个类的一个实例,它的作用跟 City(*delhi_data) 是一样的。

_asdict() 把具名元组以 collections.OrderedDict 的形式返回,我们可以利用它来把元组里的信息友好地呈现出来。

1
2
3
4
5
6
7
8
from collections import namedtuple
User = namedtuple("USER", ["name", "age", "city", "height"])
user_1 = User(name="雷鸣", age=21, city="北京", height="175")
print("user_1", user_1.name, user_1.age, user_1.city, user_1.height)
user_1 雷鸣 21 北京 175

user_1._fields
Out[4]: ('name', 'age', 'city', 'height')

使用

定义

代码

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
# namedtuple: 生成可以使用名字来访问元素内容的tuple子类
# -*- coding: utf-8 -*
from collections import namedtuple

User = namedtuple("USER", ["name", "age", "city", "height"])
user_1 = User(name="雷鸣", age=21, city="北京", height="175")
print("user_1", user_1.name, user_1.age, user_1.city, user_1.height)

user_tuple = ("雷姆", 17, '异世界')
user_2 = User(*user_tuple, "172")
print("user_2", user_2.name, user_2.age, user_2.city, user_2.height)

user_dict = {
"name": "雷玖",
"age": "17",
"city": "异世界",
}
user_3 = User(**user_dict, height="172")
print("user_3", user_3.name, user_3.age, user_3.city, user_3.height)

user_tuple_2 = ("雷姆", 17, '异世界', "172")
user_list_2 = ["雷姆", 17, '异世界', "172"]
user_dict_2 = {
"name": "雷玖",
"age": "17",
"city": "异世界",
"height": "172"
}
user_4 = User._make(user_dict_2.values())
print("user_4", user_4.name, user_4.age, user_4.city, user_4.height)
user_5 = User._make(user_tuple_2)
print("user_5", user_5.name, user_5.age, user_5.city, user_5.height)
user_6 = User._make(user_list_2)
print("user_6", user_6.name, user_6.age, user_6.city, user_6.height)

输出

1
2
3
4
5
6
user_1 雷鸣 21 北京 175
user_2 雷姆 17 异世界 172
user_3 雷玖 17 异世界 172
user_4 雷玖 17 异世界 172
user_5 雷姆 17 异世界 172
user_6 雷姆 17 异世界 172

访问

代码

1
2
3
4
5
6
7
8
9
from collections import namedtuple
User = namedtuple("USER", ["name", "age", "city", "height"])
user_tuple = ("雷姆", 17, '异世界')
user_2 = User(*user_tuple, "172")
print(user_2._asdict())

item = ('a', 'b', 'a', 'a', 5)
for it in set(item):
print(f'{it} 出现了{item.count(it)}次')

输出

1
2
3
4
OrderedDict([('name', '雷姆'), ('age', 17), ('city', '异世界'), ('height', '172')])
a 出现了3
5 出现了1
b 出现了1

字典

介绍

dict 类型不但在各种程序里广泛使用,它也是 Python 语言的基石。

跟它有关的内置函数都在 __builtins__.__dict__模块中。

正是因为字典至关重要,Python 对它的实现做了高度优化,而散列表则是字典类型性能出众的根本原因。

collections.abc 模块中有 MappingMutableMapping 这两个抽象基类

然而,非抽象映射类型一般不会直接继承这些抽象基类,它们会直接对 dict 或是 collections.UserDict 进行扩展。

标准库里的所有映射类型都是利用 dict 来实现的,因此它们有个共同的限制,即只有可散列的数据类型才能用作这些映射里的键

关于可散列类型的定义有这样一段话:

如果一个对象是可散列的,那么在这个对象的生命周期中,它的散列值是不变的,而且这个对象需要实现 __hash__() 方法。另外可散列对象还要有 __qe__() 方法,这样才能跟其他键做比较。如果两个可散列对象是相等的,那么它们的散列值一定是一样的

一般来讲用户自定义的类型的对象都是可散列的,散列值就是它们的 id() 函数的返回值,所以所有这些对象在比较的时候都是不相等的

创建字典的不同方式:

1
2
3
4
5
6
7
>>> a = dict(one=1, two=2, three=3)
>>> b = {'one': 1, 'two': 2, 'three': 3}
>>> c = dict(zip(['one', 'two', 'three'], [1, 2, 3]))
>>> d = dict([('two', 2), ('one', 1), ('three', 3)])
>>> e = dict({'three': 3, 'one': 1, 'two': 2})
>>> a == b == c == d == e
True

除了这些字面句法和灵活的构造方法之外,字典推导(dict comprehension)也可以用来建造新 dict

字典推导(dictcomp)可以从任何以键值对作为元素的可迭代对象中构建出字典。

映射类型的方法其实很丰富。表 3-1 为我们展示了 dictdefaultdictOrderedDict 的常见方法,后面两个数据类型是 dict 的变种,位于 collections 模块内。

字典的变种

  1. collections.OrderedDict: 这个类型在添加键的时候会保持顺序,因此键的迭代次序总是一致的。OrderedDictpopitem 方法默认删除并返回的是字典里的最后一个元素,但是如果像 my_odict.popitem(last=False) 这样调用它,那么它删除并返回第一个被添加进去的元素。
  2. collections.ChainMap: 该类型可以容纳数个不同的映射对象,然后在进行键查找操作的时候,这些对象会被当作一个整体被逐个查找,直到键被找到为止。
  3. collections.Counter: 这个映射类型会给键准备一个整数计数器。每次更新一个键的时候都会增加这个计数器。所以这个类型可以用来给可散列表对象计数,或者是当成多重集来用——多重集合就是集合里的元素可以出现不止一次。

OrderedDictChainMapCounter 这些开箱即用的类型不同,UserDict 是让用户继承写子类的。

dict的实现及其导致的结果

键必须是可散列的

一个可散列的对象必须满足以下要求。

(1) 支持 hash() 函数,并且通过 __hash__() 方法所得到的散列值是不变的。

(2) 支持通过 __eq__() 方法来检测相等性。

(3) 若 a == b 为真,则 hash(a) == hash(b) 也为真。

所有由用户自定义的对象默认都是可散列的,因为它们的散列值由 id() 来获取,而且它们都是不相等的。

字典在内存上的开销巨大

由于字典使用了散列表,而散列表又必须是稀疏的,这导致它在空间上的效率低下。

在用户自定义的类型中,__slots__ 属性可以改变实例属性的存储方式,由 dict变成 tuple

键查询很快

dict 的实现是典型的空间换时间:字典类型有着巨大的内存开销,但它们提供了无视数据量大小的快速访问——只要字典能被装在内存里

键的次序取决于添加顺序

当往 dict 里添加新键而又发生散列冲突的时候,新键可能会被安排存放到另一个位置。

往字典里添加新键可能会改变已有键的顺序

无论何时往字典里添加新的键,Python 解释器都可能做出为字典扩容的决定。扩容导致的结果就是要新建一个更大的散列表,并把字典里已有的元素添加到新表里。

defaultdict默认值词典

错误方式

代码

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
# 错误方式一
student_grades = {}
grades = [('elliot', 91), ('neelam', 98),
('bianca', 81), ('elliot', 88)]
for name, grade in grades:
if name not in student_grades:
student_grades[name] = []
student_grades[name].append(grade)
print(student_grades)

# 错误方式二
student_grades = {}
for name, grade in grades:
student_grades.setdefault(name, [])
student_grades[name].append(grade)
print(student_grades)

输出

1
2
{'elliot': [91, 88], 'neelam': [98], 'bianca': [81]}
{'elliot': [91, 88], 'neelam': [98], 'bianca': [81]}

正确方式

在这种情况下,你将创建一个defaultdict,它使用不带参数的list构造函数作为默认方法。没有参数的list返回一个空列表,因此如果名称不存在则defaultdict调用list(),然后再把学生成绩添加上。如果你想更炫一点,你也可以使用lambda函数作为值来返回任意常量。

在用户创建 defaultdict 对象的时候,就需要给它配置一个为找不到的键创造默认值的方法。

实例化一个 defaultdict 的时候,需要给构造方法提供一个可调用对象

list 构造方法作为 default_factory 来创建一个 defaultdict

defaultdict 里的 default_factory 只会在 __getitem__ 里被调用,在其他的方法里完全不会发挥作用。比如,dd 是个 defaultdictk 是个找不到的键, dd[k] 这个表达式会调用 default_factory 创造某个默认值,而 dd.get(k) 则会返回 None

所有的映射类型在处理找不到的键的时候,都会牵扯到 __missing__ 方法。这也是这个方法称作“missing”的原因

如果有一个类继承了 dict,然后这个继承类提供了 __missing__ 方法,那么在 __getitem__ 碰到找不到的键的时候,Python 就会自动调用它

__missing__ 方法只会被 __getitem__ 调用

k in my_dict.keys() 这种操作在 Python 3 中是很快的,而且即便映射类型对象很庞大也没关系。这是因为 dict.keys() 的返回值是一个“视图”。视图就像一个集合,而且跟字典类似的是,在视图里查找一个元素的速度很快。

代码

1
2
3
4
5
# 正确方式
from collections import defaultdict
student_grades = defaultdict(list)
[student_grades[name].append(grade) for name, grade in grades]
print(student_grades)

输出

1
defaultdict(<class 'list'>, {'elliot': [91, 88], 'neelam': [98], 'bianca': [81]})

统计缺失多少键

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
class CountMissing:
...: def __init__(self):
...: self.added = 0
...: def missing(self):
...: self.added += 1
...: return 0
...:
current = {'green':12, 'blue':3}
increments = [('red',5),('blue',17),('orange',9)]
counter = CountMissing()
from collections import defaultdict
result = defaultdict(counter.missing, current)
for key,amount in increments:
...: result[key] += amount
...:
print(counter.added)
2

其它默认值

代码

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
# 错误方式
cowboy = {'age': 32, 'horse': 'mustang', 'hat_size': 'large'}
if 'name' in cowboy:
name = cowboy['name']
else:
name = 'The Man with No Name'
print(name)
# 如果你想在仍然访问name的key时使用默认值更新字典
# 错误方式
if 'name' not in cowboy:
cowboy['name'] = 'The Man with No Name'
name = cowboy['name']
print(name)

# 正确方式
# get()执行与第一种方法相同的操作,但现在它们会自动处理。如果key存在,则返回适当的值。否则,将返回默认值
name = cowboy.get('name', 'The Man with No Name')
print(name)
# 正确方式
name = cowboy.setdefault('name', 'The Man with No Name')
print(name)

from collections import defaultdict
dic = defaultdict(lambda :'0a')
dic['a']
Out[41]: '0a'

输出

1
2
3
4
The Man with No Name
The Man with No Name
The Man with No Name
The Man with No Name

有序词典OrderedDict

概念

OrderedDict类似于正常的词典,只是它记住了元素插入的顺序,当在有序的词典上迭代时,返回的元素就是它们第一次添加的顺序。

class collections.OrderedDict,返回已给dict的子类,支持常规的dict的方法,OrderedDict是一个记住元素首次插入顺序的词典,如果一个元素重写已经存在的元素,那么原始的插入位置保持不变,如果删除一个元素再重新插入,那么它就在末尾。

使用

使用dict时,Key是无序的。在对dict做迭代时,我们无法确定Key的顺序。如果要保持Key的顺序,可以用OrderedDict,OrderedDict的Key会按照插入的顺序排列,不是Key本身排序

例子1

代码

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
# 例子1
from collections import OrderedDict

odd0 = {'banana': 3, 'apple': 4}
od1 = OrderedDict({'banana': 3, 'apple': 4})
od2 = OrderedDict({'apple': 4, 'banana': 3})
print(od1 == od2)
print(od1 == odd0)

# 例子2
from collections import OrderedDict

od1 = OrderedDict({'banana': 3, 'apple': 4})
# OrderedDict.popitem(last=True),popitem方法返回和删除一个(key,value)对,如果last=True,就以LIFO方式执行,否则以FIFO方式执行。
od1.popitem(False)
print(od1)
od1.pop('apple') # 这里必须提供key
print(od1)

输出

1
2
3
4
False
True
OrderedDict([('apple', 4)])
OrderedDict()

例子2

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
# 例子3
from collections import OrderedDict
od1 = OrderedDict({'banana': 3, 'apple': 4})
od1.move_to_end('banana')
print(od1)

# 例子4
# OrderedDict可以实现一个FIFO(先进先出)的dict,当容量超出限制时,先删除最早添加的Key
from collections import OrderedDict
class LastUpdatedOrderedDict(OrderedDict):
def __init__(self, capacity):
super(LastUpdatedOrderedDict, self).__init__()
self._capacity = capacity

def __setitem__(self, key, value):
containsKey = 1 if key in self else 0
if len(self) - containsKey >= self._capacity:
last = self.popitem(last=False)
print('remove:', last)
if containsKey:
del self[key]
print('set:', (key, value))
else:
print('add:', (key, value))
OrderedDict.__setitem__(self, key, value)
1
OrderedDict([('apple', 4), ('banana', 3)])

ChainMap

原始的映射对象被存放在一个列表中构成一个字典序列self.maps

ChainMap 中查询某个键时,会对原始的映射对象依次查询,直至找到这个键,若未找到,则默认引发 KeyError 异常。

ChainMap 中进行插入、更新、删除时,只会对原始映射中的第一个映射进行操作

ChainMap 对象除了 maps 属性外,还具有一个 parents 属性和 new_child(m=None) 方法。

parents 属性返回了一个不包含原始映射中的第一个映射的 ChainMap 对象,对应源码中 self.class(self.maps[1:]) ,其效果和 ChainMap(d.maps[1:]) 相同

new_child(m=None) 方法返回一个包含指定映射 m(未指定时,为空字典)及其他原有映射的 ChainMap 对象,其中指定映射位于底层 maps 列表首位,在其他原有映射之前。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
# ChainMap 类是为了将多个映射快速的链接到一起,这样它们就可以作为一个单元处理。它通常比创建一个新字典和多次调用 update() 要快很多

from collections import ChainMap
a = {'a': 1, 'b': 2}
b = {'c': 3}
c = ChainMap(a, b) # 如果有重复的key,以第一次为准
print(c)
# 将数据以列表形式展现,实际上是将链接指向原来的实现
print(c.maps)
print(c.maps[0]['b'])

ChainMap({'a': 1, 'b': 2}, {'c': 3})
[{'a': 1, 'b': 2}, {'c': 3}]
2

# 简单应用
一个Python命令中,如果在命令行中输入参数则使用该参数,没有则从OS环境变量中获取,如果还没有再取自定义默认值
import os
import argparse
from collections import ChainMap
defaults = {'color': 'red', 'user': 'guest'}
parser = argparse.ArgumentParser()
parser.add_argument('-u', '--user')
parser.add_argument('-c', '--color')
namespace = parser.parse_args()
command_line_args = {k: v for k, v in vars(namespace).items() if v}
combined = ChainMap(command_line_args, os.environ, defaults)
print(combined['color'])
print(combined['user'])

red
guest

使用 ChainMap 对象作为嵌套上下文

我们在上面提到了 new_child(m=None) 可用于创建子上下文,这个是一个非常便捷的方法,可以使用一个空字典或其他指定的映射来创建一个新的 ChainMap 对象。针对这个新对象的修改不会对原有的 ChainMap 对象(可以将其理解为底层的数据结构或者基础上下文)产生影响。

1
2
3
4
5
6
7
8
9
10
11
12
13
>>> d_1 = {'name': 'bob', 'age': 25}
>>> d_2 = {'height': '175', 'weight': 120}
>>> c = ChainMap(d_1, d_2) # 创建一个基础上下文
>>> c_nc = c.new_child() # 创建一个嵌套的子上下文
>>> c_nc['skill'] = 'Python' # 在子上下文环境中进行赋值
>>> c
ChainMap({'name': 'bob', 'age': 25}, {'height': '175', 'weight': 120})
>>> c_nc
ChainMap({'skill': 'Python'}, {'name': 'bob', 'age': 25}, {'height': '175', 'weight': 120})
>>> list(c_nc)
['height', 'weight', 'name', 'age', 'skill']
>>> dict(c_nc)
{'height': '175', 'weight': 120, 'name': 'bob', 'age': 25, 'skill': 'Python'}

计数器Counter

Ps: Counter仅支持Hashable对象进行统计

定义

假如你有一长串没有标点符号或大写字母的单词,你想要计算每个单词出现的次数。

你可以使用字典或defaultdict增加计数,但collections.Counter提供了一种更清晰,更方便的方法。

Counter是dict的子类,它使用0作为任何缺失元素的默认值,并且更容易计算对象的出现次数:

1
2
3
4
5
6
7
from collections import Counter
words = "if there was there was but if there was not there was not".split()
# 或者 counts = Counter(if=2,there=4,was=4,not=2,but=1) # 传进元组
counts = Counter(words)
print(counts)

>>> Counter({'there': 4, 'was': 4, 'if': 2, 'not': 2, 'but': 1})

遍历元素

遍历所有元素

1
2
3
4
5
6
7
8
9
10
# 当你将单词列表传递给Counter时,它会存储每个单词以及该单词在列表中出现的次数。
# 如果你好奇两个最常见的词是什么?只需使用.most_common():
print(counts.most_common(2))

# 遍历所有元素
for k, v in counts.items():
print(k, v)

# 遍历打印所有元素
print(sorted(counts.elements()))
1
2
3
4
5
6
7
8
9
[('there', 4), ('was', 4)]

if 2
there 4
was 4
but 1
not 2

['but', 'if', 'if', 'not', 'not', 'there', 'there', 'there', 'there', 'was', 'was', 'was', 'was']

更新元素

1
2
3
4
5
6
7
8
9
10
11
12
# update(增加元素)
counts.update("if you are here".split())
print(counts)

# subtract(原来的元素减去新传入的元素)
counts.subtract(['if']) # if的次数减一
print(counts)

# 删除元素
counts['but'] = 0
del counts['but']
print(counts)
1
2
3
Counter({'there': 4, 'was': 4, 'if': 3, 'not': 2, 'but': 1, 'you': 1, 'are': 1, 'here': 1})
Counter({'there': 4, 'was': 4, 'if': 2, 'not': 2, 'but': 1, 'you': 1, 'are': 1, 'here': 1})
Counter({'there': 4, 'was': 4, 'if': 2, 'not': 2, 'you': 1, 'are': 1, 'here': 1})

类集合操作

1
2
3
4
5
6
c = Counter(a=3, b=1)
d = Counter(a=1, b=2)
print(c + d) # 相加
print(c - d) # 相减,如果小于等于0,删去
print(c & d) # 求最小
print(c | d) # 求最大
1
2
3
4
Counter({'a': 4, 'b': 3})
Counter({'a': 2})
Counter({'a': 1, 'b': 1})
Counter({'a': 3, 'b': 2})

子类化UserDict

就创造自定义映射类型来说,以 UserDict 为基类,总比以普通的 dict 为基类要来得方便。

而更倾向于从 UserDict 而不是从 dict 继承的主要原因是,后者有时会在某些方法的实现上走一些捷径,导致我们不得不在它的子类中重写这些方法,但是 UserDict 就不会带来这些问题。

值得注意的地方是,UserDict 并不是 dict 的子类,但是 UserDict 有一个叫作 data 的属性,是 dict 的实例,这个属性实际上是 UserDict 最终存储数据的地方。

双向队列deque

定义

deque是栈和队列的一种广义实现,deque是”double-end queue”的简称;deque支持线程安全、有效内存地以近似O(1)的性能在deque的两端插入和删除元素,尽管list也支持相似的操作,但是它主要在固定长度操作上的优化,从而在pop(0)和insert(0,v)(会改变数据的位置和大小)上有O(n)的时间复杂度。

常用方法

deque支持如下方法,

append(x), 将x添加到deque的右侧;

appendleft(x), 将x添加到deque的左侧;

clear(), 将deque中的元素全部删除,最后长度为0;

count(x), 返回deque中元素等于x的个数;

extend(iterable), 将可迭代变量iterable中的元素添加至deque的右侧;

extendleft(iterable), 将变量iterable中的元素添加至deque的左侧,往左侧添加序列的顺序与可迭代变量iterable中的元素相反;

pop(), 移除和返回deque中最右侧的元素,如果没有元素,将会报出IndexError;

popleft(), 移除和返回deque中最左侧的元素,如果没有元素,将会报出IndexError;

remove(value), 移除第一次出现的value,如果没有找到,报出ValueError;

reverse(), 反转deque中的元素,并返回None;

rotate(n), 从右侧反转n步,如果n为负数,则从左侧反转,d.rotate(1)等于d.appendleft(d.pop());

maxlen, 只读的属性,deque的最大长度,如果无解,就返回None;

除了以上的方法之外,deque还支持迭代、序列化、len(d)、reversed(d)、copy.copy(d)、copy.deepcopy(d),通过in操作符进行成员测试和下标索引,索引的时间复杂度是在两端是O(1),在中间是O(n),为了快速获取,可以使用list代替。

​ index(查找某个元素的索引位置)

insert(在指定位置插入元素)

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
>>> from collections import deque
>>> d = deque('ghi')# 新建一个deque,有三个元素
>>> for ele in d:# 遍历deque
... print ele.upper()
...
...
G
H
I
>>> d.append('j')# deque右侧添加一个元素
>>> d.appendleft('f')# deque左侧添加一个元素
>>> d# 打印deque
deque(['f', 'g', 'h', 'i', 'j'])
>>> d.pop()# 返回和移除最右侧元素
'j'
>>> d.popleft()# 返回和移除最左侧元素
'f'
>>> list(d)# 以列表形式展示出deque的内容
['g', 'h', 'i']
>>> d[0]# 获取最左侧的元素
'g'
>>> d[-1]# 获取最右侧的元素
'i'
>>> list(reversed(d))# 以列表形式展示出倒序的deque的内容
['i', 'h', 'g']
>>> 'h' in d# 在deque中搜索
True
>>> d.extend('jkl')# 一次添加多个元素
>>> d
deque(['g', 'h', 'i', 'j', 'k', 'l'])
>>> d.rotate(1)# 往右侧翻转
>>> d
deque(['l', 'g', 'h', 'i', 'j', 'k'])
>>> d.rotate(-1)# 往左侧翻转
>>> d
deque(['g', 'h', 'i', 'j', 'k', 'l'])
>>> deque(reversed(d))# 以逆序新建一个deque
deque(['l', 'k', 'j', 'i', 'h', 'g'])
>>> d.clear()# 清空deque
>>> d.pop()# 不能在空的deque上pop
Traceback (most recent call last):
File "<input>", line 1, in <module>
IndexError: pop from an empty deque
>>> d.extendleft('abc')# 以输入的逆序向左扩展
>>> d
deque(['c', 'b', 'a'])
1
2
3
4
5
6
7
8
9
10
11
12
13
# deque是为了高效实现插入和删除操作的双向列表,适合用于队列和栈:
# 使用list存储数据时,按索引访问元素很快,但是插入和删除元素就很慢了,因为list是线性存储,数据量大的时候,插入和删除效率很低。
>>> from collections import deque
>>> q = deque(['a', 'b', 'c'])
>>> q.append('x')
>>> q.appendleft('y')
>>> q
deque(['y', 'a', 'b', 'c', 'x'])

# deque除了实现list的append()和pop()外,还支持appendleft()和popleft(),这样就可以非常高效地往头部添加或删除元素
# deque是线程安全的,有GIL保护
# append和popleft都是原子操作

multiprocessing:这个包实现了自己的Queue,它和queue.Queue类似,是设计给进程间通信使用的。同时还有个multiprocessing.JoinableQueue类型,可以让任务管理变得更方便。

asyncio:python3.4新提供的包,里面有Queue、LifoQueue、PriorityQueue和JoinableQueue,这些类受到queue和mulitiprocessing模块的影响,但是为异步编程里的任务管理提供了便利。

子类化UserDict

UserDict 封装了字典对象,简化了字典子类化
UserList 封装了列表对象,简化了列表子类化
UserString 封装了列表对象,简化了字符串子类化