目录
- 前言
- namedtuple
- namedtuple的由来
- namedtuple的格式
- namedtuple声明以及实例化
- namedtuple的方法和属性
- OrderedDict
- popitem(last=True)
- move_to_end(key, last=True)
- 支持reversed
- 相等测试敏感
- defaultdict
- 小例子1
- 小例子2
- 小例子3
- Counter对象
- 创建方式
- elements()
- most_common([n])
- 应用场景
- deque([iterable[, maxlen]])
- deque的方法
- deque 用法
前言
这个模块实现了特定目标的容器,以提供Python标准内建容器dict,list,set, 和tuple的替代选择。
这个模块提供了以下几个函数
函数 | 作用 |
namedtuple() | 创建命名元组子类的工厂函数 |
deque | 类似列表(list)的容器,实现了在两端快速添加(append)和弹出(pop) |
ChainMap | 类似字典(dict)的容器类,将多个映射集合到一个视图里面 |
Counter | 字典的子类,提供了可哈希对象的计数功能 |
OrderedDict | 字典的子类,保存了他们被添加的顺序 |
defaultdict | 字典的子类,提供了一个工厂函数,为字典查询提供一个默认值 |
UserDict | 封装了字典对象,简化了字典子类化 |
UserList | 封装了列表对象,简化了列表子类化 |
UserString | 封装了字符串对象,简化了字符串子类化 |
namedtuple
namedtuple的由来
因为元组的局限性:不能为元组内部的数据进行命名,所以往往我们并不知道一个元组所要表达的意义,所以引入namedtuple这个工厂函数,来构造一个带字段名的元组。namedtuple继承自tuple类
命名元组赋予每个位置一个含义,提供可读性。它们可以用于任何普通元组,并添加了通过名字获取值的能力,通过索引值也是可以的。
namedtuple的格式
collections.namedtuple(typename, field_names, *, rename=False, defaults=None, module=None)
- typename: 返回一个新的元组子类,名为typename。这个新的子类用于创建类元组的对象,可以通过字段名来获取属性值,同样也可以通过索引和迭代获取值。
- field_names: 像['x', 'y'] 一样的字符串序列。另外field_names可以是一个纯字符串,用空白或逗号分隔开元素名,比如 'x y' 或者 'x, y' 。
- rename=False: 如果rename为true,无效字段名会自动转换成_+索引值,比如 ['abc', 'def', 'ghi', 'abc'] 转换成 ['abc', '_1', 'ghi', '_3'] , 消除关键词def和重复字段名abc。
- default=None: defaults 可以为 None 或者是一个默认值的 iterable 。default默认值赋值跟我们平常的默认值相反,default默认值是从最右边开始,比如field_names中提供了3个字段['x', 'y', 'z'],default默认值设置为(1, 2),那么我们必须为x指定1个值,y默认值为1,z默认值为2
- module=None: 如果 module 值有定义,命名元组的module属性值就被设置。
namedtuple声明以及实例化
我们首先创建一个User类,定义3个字段name、age、height,并给age设置默认值为18,给height设置了默认值180
User = namedtuple('User', ['name', 'age', 'height'], defaults=(, 180)) | |
print(User.__mro__) |
我们查看结果
(<class '__main__.User'>, <class 'tuple'>, <class 'object'>)
可以看到我们声明的User类是继承于tuple,接下来我们创建实例
user = User(name='jkc') | |
user = User(name='jkc2', age=20, height=198) | |
print(user) | |
print(user) | |
print(user.name) | |
print(user.age) |
运行结果为
User(name='jkc', age=18, height=180)
User(name='jkc2', age=20, height=198)
jkc
20
namedtuple的方法和属性
命名元组还支持三个额外的方法和两个属性。为了防止字段名冲突,方法和属性以下划线开始。
_make(iterable)类方法从存在的序列或迭代实例创建一个新实例。
'jkc', 25, 190] | t = [|
User._make(t) | |
User(name='jkc', age=25, height=190) |
_asdict()返回一个新的 dict ,它将字段名称映射到它们对应的值
>>> user = User(name='jkc4', age=28, height=200) | |
>>> user._asdict() | |
{'name': 'jkc', 'age': 28, 'height': 200} |
_replace(**kwargs)返回一个新的命名元组实例,并将指定域替换为新的值
'jkc5', age=30, height=210) | user = User(name=|
18) | user._replace(age=|
User(name='jkc', age=30, height=210) |
_fields字符串元组列出了字段名。用于提醒和从现有元组创建一个新的命名元组类型
user._fields | |
('name', 'age', 'height') |
_field_defaults字典将字段名称映射到默认值。
User._field_defaults | |
{'name': 'jkc', 'age':, 'height': 180} |
转换一个字典到命名元组,使用 ** 两星操作符
'name': 'jkc', 'age': 18, 'height': 180} | d = {|
User(**d) | |
User(name='jkc', age=18, height=180) |
OrderedDict
有序字典就像常规字典一样,但有一些与排序操作相关的额外功能。由于内置的dict类获得了记住插入顺序的能力(在Python 3.7中保证了这种新行为),它们变得不那么重要了。
与dict类的区别
- 常规的dict被设计为非常擅长映射操作。 跟踪插入顺序是次要的
- OrderedDict擅长重新排序操作。 空间效率、迭代速度和更新操作的性能是次要的。
- 算法上,OrderedDict可以比dict更好地处理频繁的重新排序操作。 这使其适用于跟踪最近的访问(例如在 LRU cache 中)。
- 对于OrderedDict,相等操作检查匹配顺序。
- OrderedDict类的popitem()方法有不同的签名。它接受一个可选参数来指定弹出哪个元素。
- OrderedDict类有一个move_to_end()方法,可以有效地将元素移动到任一端。
- Python 3.8之前, dict 缺少__reversed__()方法。
popitem(last=True)
有序字典的popitem()方法移除并返回一个(key, value)键值对。 如果last值为真,则按LIFO后进先出的顺序返回键值对,否则就按FIFO先进先出的顺序返回键值对。
from collections import OrderedDict | |
d = OrderedDict({'status':, 'message': 'success'}) | |
print(f'原始的有序字典: {d}') | |
print('被删除的键值对是: ', d.popitem(last=True)) # 后进先出 | |
print(f'被删除后的有序字典: {d}') | |
# 结果 | |
原始的有序字典: OrderedDict([('status',), ('message', 'success')]) | |
被删除的键值对是: ('message', 'success') | |
被删除后的有序字典: OrderedDict([('status',)]) | |
from collections import OrderedDict | |
d = OrderedDict({'status':, 'message': 'success'}) | |
print(f'原始的有序字典: {d}') | |
print('被删除的键值对是: ', d.popitem(last=False)) # 先进先出 | |
print(f'被删除后的有序字典: {d}') | |
# 结果 | |
原始的有序字典: OrderedDict([('status',), ('message', 'success')]) | |
被删除的键值对是: ('status',) | |
被删除后的有序字典: OrderedDict([('message', 'success')]) |
move_to_end(key, last=True)
将现有key移动到有序字典的任一端。 如果last为真值(默认)则将元素移至末尾;如果last为假值则将元素移至开头。如果key不存在则会触发KeyError:
d = OrderedDict({'status':, 'message': 'success'}) | |
d.move_to_end('status', last=True) | |
print('移动后的字典: ', d) | |
d.move_to_end('status', last=False) | |
print('移动后的字典', d) | |
# 结果 | |
移动后的字典: OrderedDict([('message', 'success'), ('status',)]) | |
移动后的字典: OrderedDict([('status',), ('message', 'success')]) |
支持reversed
相对于通常的映射方法,有序字典还另外提供了逆序迭代的支持,通过reversed()。
d = OrderedDict({'status':, 'message': 'success'}) | |
print({key: value for key, value in reversed(d.items())}) | |
# 结果 | |
{'message': 'success', 'status':} |
相等测试敏感
OrderedDict之间的相等测试是顺序敏感的
d = OrderedDict({'status': 200, 'message': 'success'}) | |
d = OrderedDict({'message': 'success', 'status': 200}) | |
d = {'status': 200, 'message': 'success'} | |
d = {'message': 'success', 'status': 200} | |
print('OrderedDict之间的比较结果: ', d == d2) | |
print('dict之间的比较结果: ', d == d4) | |
print('OrderedDict与dict的比较结果: ', d == d3 == d4) | |
# 结果 | |
OrderedDict之间的比较结果: False | |
dict之间的比较结果: True | |
OrderedDict与dict的比较结果: True |
defaultdict
返回一个新的类似字典的对象。defaultdict是内置dict类的子类。它重载了一个方法并添加了一个可写的实例变量。其余的功能与dict类相同
defaultdict的作用我们看名字就知道defaultdict的作用是为字典提供一个默认的值,我们正常情况下访问一个字典的key,如果字典中没有这个key会报错
dict = {} | |
dict['name'] | |
Traceback (most recent call last): | |
File "<pyshell#>", line 1, in <module> | |
dict['name'] | |
KeyError: 'name' | |
>>> |
此时我们就可以使用defaultdict,它包含一个名为default_factory的属性,构造时,第一个参数用于为该属性提供初始值,默认为None。
这个default_factory可以是list、set、str,也可以是自定义的函数,作用是当key不存在时,返回的是工厂函数的默认值,比如list对应[ ],str对应的是空字符串,set对应set( ),int对应0
dict = defaultdict(int) | |
dict = defaultdict(set) | |
dict = defaultdict(str) | |
dict = defaultdict(list) | |
print(dict['name']) | |
print(dict['name']) | |
print(dict['name']) | |
print(dict['name']) |
输出
0
set()
[]
小例子1
使用list作为default_factory,我们可以很轻松地将(键-值对组成的)序列转换为(键-列表组成的)字典:
from collections import defaultdict | |
'yellow',), ('blue', 2), ('yellow', 3), ('blue', 4), ('red', 1)] | s = [(|
list) | d = defaultdict(|
for k, v in s: | |
d[k].append(v) | |
sorted(d.items()) | |
[('blue', [, 4]), ('red', [1]), ('yellow', [1, 3])] |
小例子2
设置default_factory为int,使defaultdict用于计数
'aiibiicii' | s =|
int) | d = defaultdict(|
for k in s: | |
d[k] += | |
sorted(d.items()) | |
[('a',), ('b', 1), ('c', 1), ('i', 6)] |
小例子3
如果你需要自己定义一个返回值,你可以创建1个函数,设置自定义的返回值
def constant_factory(value): | |
return lambda: value | |
d = defaultdict(constant_factory('success')) | |
d.update(status=) | |
var = d['message'] | |
print(sorted(d.items())) | |
# 输出 | |
[('message', 'success'), ('status',)] |
Counter对象
它一个计数器工具提供快速和方便的计数。
它是dict的子类,用于计数可哈希对象。它是一个集合,元素像字典键(key)一样存储,它们的计数存储为值。计数可以是任何整数值,包括0和负数。
创建方式
元素从一个iterable被计数或从其他的mapping (or counter)初始化:
c = Counter() # a new, empty counter | |
c = Counter('gallahad') # a new counter from an iterable | |
c = Counter({'red':, 'blue': 2}) # a new counter from a mapping | |
c = Counter(cats=, dogs=8) # a new counter from keyword args |
如果引用的键没有任何记录,就返回一个0,而不是弹出一个 KeyError
'eggs', 'ham']) | c = Counter([|
'bacon'] | c[
作为dict的子类,Counter继承了记住插入顺序的功能。Counter对象进行数学运算时同样会保持顺序。 结果会先按每个元素在运算符左边的出现时间排序,然后再按其在运算符右边的出现时间排序。
elements()
返回一个迭代器,其中每个元素将重复出现计数值所指定次。 元素会按首次出现的顺序返回。 如果一个元素的计数值小于一,elements()将会忽略它。
2, c=0, d=-2) | c = Counter(a=, b=|
list(c.elements()) | |
['a', 'a', 'a', 'a', 'b', 'b'] |
most_common([n])
返回一个列表,其中包含n个最常见的元素及出现次数,按常见程度由高到低排序。 如果n被省略或为None,most_common()将返回计数器中的所有元素。 计数值相等的元素按首次出现的顺序排序:
>>> Counter('abracadabra').most_common() | |
[('a',), ('b', 2), ('r', 2), ('c', 1), ('d', 1)] | |
>>> Counter('abracadabra').most_common() | |
[('a',), ('b', 2)] |
应用场景
Counter对象一般有以下两种应用场景
1. 统计单词在列表中的出现次数
count = Counter() | |
list = ['red', 'blue', 'red', 'green', 'blue', 'blue'] | |
for word in list: | |
count[word] += | |
count | |
Counter({'blue':, 'red': 2, 'green': 1}) |
count[word]因为没有在Counter对象中,所以默认情况下会给他赋值为0,因此可以统计出单词出现的次数
2. 找出文件中最常见的十个单词
import re | |
r'\w+', open('log.txt').read().lower()) | words = re.findall(|
Counter(words).most_common() | |
[('the',), ('and', 822), ('to', 810), ('of', 799), ('i', 688), | |
('you',), ('a', 508), ('my', 500), ('yes', 406), ('in', 318)] |
deque([iterable[, maxlen]])
返回一个新的双向队列对象,从左到右初始化(用方法 append()) ,从iterable(迭代对象) 数据创建。如果 iterable 没有指定,新队列为空。
Deque队列是由栈或者queue队列生成的。Deque 支持线程安全,内存高效添加(append)和弹出(pop),从两端都可以,两个方向的大概开销都是 O(1) 复杂度。
虽然list对象也支持类似操作,不过这里优化了定长操作和pop(0)和insert(0, v)的开销。它们引起O(n)内存移动的操作,改变底层数据表达的大小和位置。
如果maxlen没有指定或者是None,deques可以增长到任意长度。否则,deque就限定到指定最大长度。一旦限定长度的deque满了,当新项加入时,同样数量的项就从另一端弹出。
deque的方法
双向队列(deque)对象支持很多方法,大部分方法list都有
方法名 | 作用 |
append(x) | 添加 x 到右端 |
appendleft(x) | 添加 x 到左端 |
clear() | 移除所有元素,使其长度为0 |
copy() | 创建一份浅拷贝 |
count(x) | 计算 deque 中元素等于 x 的个数 |
extend(iterable) | 扩展deque的右侧,通过添加iterable参数中的元素 |
extendleft(iterable) | 扩展deque的左侧,通过添加iterable参数中的元素。注意,左添加时,在结果中iterable参数中的顺序将被反过来添加 |
index(x[, start[, stop]]) | 返回 x 在 deque 中的位置(在索引 start 之后,索引 stop 之前)。 返回第一个匹配项,如果未找到则引发 ValueError |
insert(i, x) | 在位置 i 插入 x,如果插入会导致一个限长 deque 超出长度 maxlen 的话,就引发一个 IndexError。 |
pop() | 移去并且返回一个元素,deque 最右侧的那一个。 如果没有元素的话,就引发一个 IndexError |
popleft() | 移去并且返回一个元素,deque 最左侧的那一个。 如果没有元素的话,就引发 IndexError |
remove(value) | 移除找到的第一个 value。 如果没有的话就引发 ValueError |
reverse() | 将deque逆序排列。返回 None 。 |
rotate(n=1) | 向右循环移动 n 步。 如果 n 是负数,就向左循环。如果deque不是空的,向右循环移动一步就等价于d.appendleft(d.pop()), 向左循环一步就等价于d.append(d.popleft())。 |
maxlen | Deque的最大尺寸,如果没有限定的话就是 None |
deque 用法
① linux下查看最新日志的命令是:tail -n 2 test.log,deque也可以实现同样的功能
def tail(filename, n=): | |
with open(filename) as f: | |
return deque(f, n) |
② 维护一个近期添加元素的序列,通过从右边添加和从左边弹出
def moving_average(iterable, n=): | |
# moving_average([, 30, 50, 46, 39, 44]) --> 40.0 42.0 45.0 43.0 | |
# http://en.wikipedia.org/wiki/Moving_average | |
it = iter(iterable) | |
d = deque(itertools.islice(it, n-)) | |
d.appendleft() | |
s = sum(d) | |
for elem in it: | |
s += elem - d.popleft() | |
d.append(elem) | |
yield s / n |