目录
4.1.4collections.Counter 字典的子类
4.1.5collections.OrderedDict 对象
之前介绍的数字类型如:整数类型、浮点数类型和复数类型,这些都能表示一个数据,这种表示单一数据的类型叫做基本数据类型,然而实际情况中存在大量同时处理多个数据的情况,这就需要将多个数据有效组织起来并统一表示,这种能够表示多个数据的类型称为组合数据类型。
序列类型是一个元素向量,元素之间存在先后关系,通过序号访问,元素之间不排他。
集合类型是一个元素集合,元素之间无序,相同元素在集合中唯一存在。
映射类型是“键-值”数据项的组合,每个元素是一个键值对,表示为(key,value)。
一:序列类型
这种类型的数据,使用相同的索引体系,即正向递增序号和反向递减序号。
序列类型有12个通用的操作符和函数。
操作符 | 描述 |
x in s | 如果x是s的元素,返回True,否则返回False |
x not in s | 如果x不是s的元素,返回True,否则返回False |
s+t | 连接s和t |
s*n或n*s | 将序列s复制n次 |
s[i] | 索引,返回序列的第i个元素 |
s[i:j] | 分片,返回包含序列s第i到j个元素的子序列(不包含第j个元素) |
s[i:j:k] | 步骤分片,返回包含序列s第i到j个元素一k为步数的子序列 |
len | 序列s的元素个数 |
min(s) | 序列s中的最小元素 |
max(s) | 序列s中的最大元素 |
s.index(x[,i[,j]]) | 序列s中从i开始到j位置中第一次出现元素x的位置 |
s.count(x) | 序列s中出现x的中次数 |
生成
range()创建整数列表
range()可以帮助我们非常方便的创建整数列表,这在开发中及其有用。语法格式为:
range([start],end ,[step])
start 参数:可选,表示起始数字。默认是 0
end 参数:必选,表示结尾数字。
step 参数:可选,表示步长,默认为 1
e = list(range(0,10,2))
print(e) #结果:[0, 2, 4, 6, 8]
f = list(range(10,0,-1))
print(f) #结果:[10, 9, 8, 7, 6, 5, 4, 3, 2, 1]
g = list(range(-10,-30,-2))
print(g) #结果:[-10, -12, -14, -16, -18, -20, -22, -24, -26, -28]
1.1列表结构类型 (List)
在之前的章节我们已经了解过 List 结构:
list1 = [1, 2, 3, 4]
#list1 只有数字
list2 = [1, 2, "a", "b"]
#list2 包含数字和字符串复合内容
list3 = [list1, list2]
#list3 在列表中包含列表
列表中的列表是Python中最强大的工具之一。列表可以包含字符串、整数以及对象等数据类型。列表还可用于实现堆栈和队列。
列表是可变的,声明后仍然可以修改。
示例:
list1 = []
# 声明一个空列表
print(list1) # 输出空列表 []
list1.append("a")
# 在列表最后位置添加元素
list1.insert(0, "b")
# 在指定位置插入元素
列表特有的函数和操作方法(下次讲)
列表应用
1.1.1用列表实现堆栈
使用列表方法实现堆栈非常容易,最后插入的最先取出(“后进先出”)。把元素添加到堆栈的顶端,使用 append() 。从堆栈顶部取出元素,使用 pop() ,不用指定索引。例如:
stack = [3, 4, 5]
stack.append(6)
stack.append(7)
print(stack)
# 输出 [3, 4, 5, 6, 7]
print(stack.pop())
# 输出 7
print(stack)
# 输出 [3, 4, 5, 6]
1.1.2用列表实现队列
列表也可以用作队列,最先加入的元素,最先取出(“先进先出”);然而,列表作为队列的效率很低。因为,在列表末尾添加和删除元素非常快,但在列表开头插入或移除元素却很慢(因为所有其他元素都必须移动一位)。
实现队列最好用 collections.deque ,可以快速从两端添加或删除元素。例如:
from collections import deque
queue = deque(["Eric", "John", "Michael"])
queue.append("Terry") # 在最后添加元素
queue.append("Graham") # 在最后添加元素
print(queue.popleft()) # 弹出第一个元素
# 输出 'Eric'
print(queue.popleft()) # 弹出第一个元素
# 输出 'John'
列表推导式
列表推导式创建列表的方式更简洁。常见的用法为,对序列或可迭代对象中的每个元素应用某种操作,用生成的结果创建新的列表;或用满足特定条件的元素创建子序列。
例如,创建平方值的列表:
squares = []
for x in range(10):
squares.append(x**2)
print(squares)
# 输出 [0, 1, 4, 9, 16, 25, 36, 49, 64, 81]
这段代码创建(或覆盖)变量 x,该变量在循环结束后仍然存在。下述方法可以无副作用地计算平方列表:
squares = list(map(lambda x: x**2, range(10)))
等价于:
squares = [x**2 for x in range(10)]
map(function, iterable)
函数接受两个参数,第一个参数是函数,返回处理过的值,第二个参数是可迭代,返回Map类型。
示例:
def foo(n: int):
return n ** n
print(list(map(foo, [1,2,3,4])))
# [1, 4, 27, 256]
此时写法变量 x
只是在推导式中有效,这种写法更简洁、易读。
列表推导式的方括号内包含以下内容:一个表达式,后面是一个 for 子句,然后,是零个或多个 for 或 if 子句。结果是由表达式依据 for 和 if 子句求值计算而得出一个新列表。 举例来说,以下列表推导式将两个列表中不相等的元素组合起来:
print([(x, y) for x in [1,2,3] for y in [3,1,4] if x != y])
# [(1, 3), (1, 4), (2, 3), (2, 1), (2, 4), (3, 1), (3, 4)]
等价于:
combs = []
for x in [1,2,3]:
for y in [3,1,4]:
if x != y:
combs.append((x, y))
print(combs)
上面两段代码中,for 和 if 的顺序相同。
表达式是元组(例如上例的 (x, y))时,必须加上括号:
print([(x, x**2) for x in range(6)])
# [(0, 0), (1, 1), (2, 4), (3, 9), (4, 16), (5, 25)]
嵌套的列表推导式
列表推导式中的初始表达式可以是任何表达式,甚至可以是另一个列表推导式。
下面这个 3x4 矩阵,由 3 个长度为 4 的列表组成:
matrix = [
[1, 2, 3, 4],
[5, 6, 7, 8],
[9, 10, 11, 12],
]
# 下面的列表推导式可以转置行列:
print([[row[i] for row in matrix] for i in range(4)])
# [[1, 5, 9], [2, 6, 10], [3, 7, 11], [4, 8, 12]]
# 如上节所示,嵌套的列表推导式基于其后的 for 求值,所以这个例子等价于:
transposed = []
for i in range(4):
transposed.append([row[i] for row in matrix])
print(transposed)
# [[1, 5, 9], [2, 6, 10], [3, 7, 11], [4, 8, 12]]
# 反过来说,也等价于:
transposed = []
for i in range(4):
# the following 3 lines implement the nested listcomp
transposed_row = []
for row in matrix:
transposed_row.append(row[i])
transposed.append(transposed_row)
print(transposed)
# [[1, 5, 9], [2, 6, 10], [3, 7, 11], [4, 8, 12]]
# 实际应用中,最好用内置函数替代复杂的流程语句。此时,zip() 函数更好用:
print(list(zip(*matrix)))
查看 zip()
表达式中星号的详细说明,参见 解包实参列表 。
1.1.3del 语句
del 语句按索引,而不是值从列表中移除元素。与返回值的 pop()
方法不同, del
语句也可以从列表中移除切片,或清空整个列表(之前是将空列表赋值给切片)。 例如:
a = [-1, 1, 66.25, 333, 333, 1234.5]
del a[0]
print(a)
[1, 66.25, 333, 333, 1234.5]
del a[2:4]
print(a)
[1, 66.25, 1234.5]
del a[:]
print(a)
[]
del 也可以用来删除整个变量:
del a
此后,再引用 a
就会报错(直到为它赋与另一个值)。后文会介绍 del 的其他用法。
1.2元组(Tuple)
我们已经介绍过两种序列类型的数据类型:列表和字符串(我没讲,诶,就是玩),这两种数据类型均可以进行索引和切片操作。
本节介绍另一种标准序列类型:元组,元组由多个用逗号隔开的值组成。
示例:
tp = "字符串", "列表", "元组"
print(tp[0])
# 输出 "字符串"
# 元组嵌套
u = tp, (1, 2, 3, 4, 5)
print(u)
# 输出:(('字符串', '列表', "元组"), (1, 2, 3, 4, 5))
元组一旦创建就不能被修改。元组不支持按索引修改,示例:
tp = "字符串", "列表", "元组"
tp[0] = "尝试修改"
# 解释器抛除类型错误:TypeError: 'tuple' object does not support item assignment
Python解释器输出时,元组都要由圆括号标注,这样才能正确地解释嵌套元组。输入时,圆括号可有可无,不过经常是必须的(如果元组是更大的表达式的一部分)。不允许为元组中的单个元素赋值,当然,可以创建含列表等可变对象的元组。
虽然,元组与列表很像,但使用场景不同,用途也不同。元组是 immutable (不可变的),一般可包含异质元素序列,通过解包(见本节下文)或索引访问(如果是 namedtuples,可以属性访问)。列表是 mutable (可变的),列表元素一般为同质类型,可迭代访问。
构造 0 个或 1 个元素的元组比较特殊:为了适应这种情况,对句法有一些额外的改变。用一对空圆括号就可以创建空元组;只有一个元素的元组可以通过在这个元素后添加逗号来构建(圆括号里只有一个值的话不够明确)。丑陋,但是有效。例如:
empty = ()
print(len(empty))
# 0
singleton = 'hello', # <-- 必须加上尾部的 , 符号才是元组,否则解释器认为是字符串
print(len(singleton))
# 1
print(singleton)
('hello',)
解包
表达式 tp = "字符串", "列表", "元组"
可以看作是元组的打包,值 "字符串", "列表", "元组"
一起被打包进 tp
变量中。
元组和列表还可以进行逆向解包操作,示例:
tp = "字符串", "列表", "元组"
z, l, y = tp
print(z) # 输出 "字符串"
print(l) # 输出 "列表"
称之为 序列解包 也是妥妥的,适用于右侧的任何序列。序列解包时,左侧变量与右侧序列元素的数量应相等。注意,多重赋值其实只是元组打包和序列解包的组合
请自行尝试list结构解包操作
二:集合类型
集合类型操作符(下来再写)20210808
操作符 | 描述 |
S - T 或 S.difference(T) | 返回一个新集合,包括在集合S中但不在集合T中的元素 |
S -= T 或 S.difference_update(T) | 更新集合S,包括在集合S中但不在集合T中的元素 |
S & T 或 S.intersection(T) | 返回一个新集合,包括同时在集合S和集合T中的元素 |
S &= T 或 S.intersection_update(T) | 更新集合S,包括同时在集合S和集合T中的元素 |
S^T 或 S.symmetric_difference(T) | 返回一个新集合,包括同时在集合S和集合T中的元素,但不包括同时在其中的元素 |
S=^T 或 S.symmetric_difference_update(T) | 更新集合S,包括同时在集合S和集合T中的元素,但不包括同时在其中的元素 |
S|T 或S.union(T) | 返回一个新集合,包括同时在集合S和集合T中的所有元素 |
S=|T 或S.update(T) | 更新集合S,包括同时在集合S和集合T中的所有元素 |
S<=T 或S.issubset(T) | 如果S与T相同或S是T的子集,返回True,否则返回False,可以用S<T判断S是否是T的真子集 |
S>=T 或S.issuperset(T) | 如果S与T相同或S是T的超集,返回True,否则返回False,可以用S>T判断S是否是T的真超集 |
上述操作符表达了集合类型的4种基本操作:交集(&)、并集(|)、差集(-)、
补集(^),操作逻辑与数字定义相同,如下图:
集合类型有10个操作函数或方法,如下表所示:
操作函数或方法 | 描述 |
S.add(x) | 如果数据项x不在集合S中,将x增加到s |
S.clear() | 移除S中的所有数据项 |
S.copy() | 返回集合S的一个副本 |
S.pop() | 随机返回集合S中的一个元素,如果S为空,产生KeyError异常 |
S.discard(x) | 如果x在集合S中,移除该元素;如果x不在集合S中,不报错 |
S.remove(x) | 如果x在集合S中,移除该元素;如果x不在集合S中,产生KeyError异常 |
S.isdisjoint(x) | 如果集合S与T没有相同元素,返回True |
len(S) | 返回集合S的元素个数 |
x in S | 如果x是集合S的元素,返回True,否则返回False |
x not in S | 如果x不是集合S的元素,返回True,否则返回False |
集合类型常用于3个场景:成员关系检测、元素去重和删除数据项。
2.1集合(Set)
集合是由不重复元素组成的无序容器。基本用法包括成员检测、消除重复元素。集合对象支持合集、交集、差集、对称差分等数学运算。
创建集合用花括号或 set() 函数。注意,创建空集合只能用 set(),不能用 {},{} 创建的是空字典,下一小节介绍数据结构:字典。
示例:
basket = {'apple', 'orange', 'apple', 'pear', 'orange', 'banana'}
print(basket) # 输出结果中重复元素已经被删除
# 输出 {'orange', 'banana', 'pear', 'apple'}
print('orange' in basket) # fast membership testing
# 输出 True
print('crabgrass' in basket)
# 输出 False
# Demonstrate set operations on unique letters from two words
a = set('abracadabra')
b = set('alacazam')
print(a)
# 输出 {'a', 'r', 'b', 'c', 'd'}, 输出结果值包含一个a字母
a - b # 计算两个set结构的差
# 输出 {'r', 'd', 'b'}
a | b # 合并两个set
# 输出 {'a', 'c', 'r', 'd', 'b', 'm', 'z', 'l'}
a & b # 计算两个set的交集
# 输出 {'a', 'c'}
a ^ b # 计算对称差集
# 输出 {'r', 'd', 'b', 'm', 'z', 'l'}
三:映射类型
字典类型的函数和方法(下次讲)
3.1字典(dict)
字典也是一种常用的 Python 內置数据类型。其他语言可能把字典称为联合内存 或联合数组。与以连续整数为索引的序列不同,字典以关键字为索引,关键字通常是字符串或数字,也可以是其他任意不可变类型。只包含字符串、数字、元组的元组,也可以用作关键字。但如果元组直接或间接地包含了可变对象,就不能用作关键字。列表不能当关键字,因为列表可以用索引、切片、append()
、extend()
等方法修改。
可以把字典理解为键值对的集合,但字典的键必须是唯一的。花括号 {}
用于创建空字典。另一种初始化字典的方式是,在花括号里输入逗号分隔的键值对,这也是字典的输出方式。
字典的主要用途是通过关键字存储、提取值。用 del
可以删除键值对。用已存在的关键字存储值,与该关键字关联的旧值会被取代。通过不存在的键提取值,则会报错。
对字典执行 list(d)
操作,返回该字典中所有键的列表,按插入次序排列(如需排序,请使用 sorted(d)
)。检查字典里是否存在某个键,使用关键字 in 。
示例:
tel = {'jack': 4098, 'sape': 4139}
tel['guido'] = 4127
print(tel)
# 输出 {'jack': 4098, 'sape': 4139, 'guido': 4127}
print(tel['jack'])
# 输出 4098
del tel['sape'] # 使用 del 删除元素
tel['irv'] = 4127
print(tel)
# 输出 {'jack': 4098, 'guido': 4127, 'irv': 4127}
print(list(tel)) # 将字段转换为列表
# 输出 ['jack', 'guido', 'irv']
print(sorted(tel)) # 排序
# 输出 ['guido', 'irv', 'jack']
print('guido' in tel)
# 输出 True
print('jack' not in tel)
# 输出 False
dict() 构造函数可以直接用键值对序列创建字典:
foo = dict([('sape', 4139), ('guido', 4127), ('jack', 4098)])
print(foo)
# 输出 {'sape': 4139, 'guido': 4127, 'jack': 4098}
字典推导式可以用任意键值表达式创建字典:
foo = {x: x**2 for x in (2, 4, 6)}
print(foo)
# 输出 {2: 4, 4: 16, 6: 36}
关键字是比较简单的字符串时,直接用关键字参数指定键值对更便捷:
foo = dict(sape=4139, guido=4127, jack=4098)
print(foo)
# 输出 {'sape': 4139, 'guido': 4127, 'jack': 4098}
四:其他类型数据类型
4.1容器数据类型( collections)
collections 不是Python语言层面的数据类型,collections是Python内置的一个模块。
import collections
# 导入 collections 模块
这个模块实现了特定目标的容器,以提供Python标准内建容器 dict , list , set , 和 tuple 的替代选择。
函数 | 释义 |
---|---|
namedtuple() | 创建命名元组子类的工厂函数 |
deque | 类似列表(list)的容器,实现了在两端快速添加(append)和弹出(pop) |
ChainMap | 类似字典(dict)的容器类,将多个映射集合到一个视图里面 |
Counter | 字典的子类,提供了可哈希对象的计数功能 |
OrderedDict | 字典的子类,保存了他们被添加的顺序 |
defaultdict | 字典的子类,提供了一个工厂函数,为字典查询提供一个默认值 |
UserDict | 封装了字典对象,简化了字典子类化 |
UserList | 封装了列表对象,简化了列表子类化 |
UserString | 封装了列表对象,简化了字符串子类化 |
4.1.1namedtuple() 命名元组的工厂函数
(collections.namedtuple(typename, field_names, *, rename=False, defaults=None, module=None))
namedtuple()
函数,接受5个参数:
collections.namedtuple(typename, field_names, *, rename=False, defaults=None, module=None)
typename
返回一个新的元组子类,名为 typename 。这个新的子类用于创建类元组的对象,可以通过字段名来获取属性值,同样也可以通过索引和迭代获取值。子类实例同样有文档字符串(类名和字段名)另外一个有用的 repr() 方法,以 name=value 格式列明了元组内容。
field_names
field_names 是一个像 ['x', 'y']
一样的字符串序列。另外 field_names 可以是一个纯字符串,用空白或逗号分隔开元素名,比如 ‘x y’ 或者 ‘x, y’ 。
任何有效的Python 标识符都可以作为字段名,除了下划线开头的那些。有效标识符由字母,数字,下划线组成,但首字母不能是数字或下划线,另外不能是关键词 keyword 比如 class, for, return, global, pass, 或 raise 。
rename
如果 rename 为真, 无效字段名会自动转换成位置名。比如 ['abc', 'def', 'ghi', 'abc']
转换成 ['abc', '_1', 'ghi', '_3']
, 消除关键词 def
和重复字段名 abc
。
defaults
defaults 可以为 None
或者是一个默认值的 iterable 。如果一个默认值域必须跟其他没有默认值的域在一起出现,defaults 就应用到最右边的参数。比如如果域名 ['x', 'y', 'z']
和默认值 (1, 2)
,那么 x
就必须指定一个参数值 ,y
默认值 1
, z
默认值 2
。
module
如果 module 值有定义,命名元组的 module 属性值就被设置。
namedtuple()
返回一个带有命名字段的子类元组.
示例:
from collections import namedtuple
Point = namedtuple('Point', ['x', 'y'])
p = Point(11, y=22) # 分别使用位置和关键词参数为元组赋值
print(p[0] + p[1]) # 像普通元组 tuple 一样使用索引
# 输出 33
x, y = p # 解包操作
print((x, y))
# 输出 (11, 22)
print(p.x + p.y) # 使用名称访问字段
# 输出 33
print(p.__repr__()) # 等效 print(p) 以 name=value 格式列明了元组内容
# 输出 Point(x=11, y=22)
namedtuple 对象还有其他有用的方法,请参考 collections.namedtuple 。
4.1.2collections.deque 对象
(class collections.deque([iterable[, maxlen]]))
deque() 函数返回一个新的双向队列对象,从左到右初始化(用方法 append()) ,从 iterable (迭代对象) 数据创建。如果 iterable 没有指定,新队列为空。
deque([iterable[, maxlen]])
函数接受2个参数,iterable
接受一个可迭代对象,maxlen
用来限制创建队列的长度,如果不限制可以创建无限长度队列。
一旦限定长度的deque满了,当新项加入时,同样数量的项就从另一端弹出。限定长度deque提供类似Unix filter tail 的功能。它们同样可以用与追踪最近的交换和其他数据池活动。
示例:
from collections import deque
d = deque('abc')
d.append("d")
# 在最后压入元素
d.appendleft("0")
# 在开始位置押入元素
for elem in d:
print(elem.upper())
# 使用 upper() 函数将小写字母转为大写字母
双向队列(deque)对象支持以下方法:
append(x) 添加 x 到右端。
appendleft(x) 添加 x 到左端。
clear() 移除所有元素,使其长度为0.
copy() 创建一份浅拷贝。
3.5 新版功能.
count(x) 计算 deque 中元素等于 x 的个数。
3.2 新版功能.
extend(iterable) 扩展deque的右侧,通过添加iterable参数中的元素。
extendleft(iterable) 扩展deque的左侧,通过添加iterable参数中的元素。注意,左添加时,在结果中iterable参数中的顺序将被反过来添加。
index(x[, start[, stop]]) 返回 x 在 deque 中的位置(在索引 start 之后,索引 stop 之前)。 返回第一个匹配项,如果未找到则引发 ValueError。
3.5 新版功能.
insert(i, x) 在位置 i 插入 x 。
如果插入会导致一个限长 deque 超出长度 maxlen 的话,就引发一个 IndexError。
3.5 新版功能.
pop() 移去并且返回一个元素,deque 最右侧的那一个。 如果没有元素的话,就引发一个 IndexError。
popleft() 移去并且返回一个元素,deque 最左侧的那一个。 如果没有元素的话,就引发 IndexError。
remove(value) 移除找到的第一个 value。 如果没有的话就引发 ValueError。
reverse() 将deque逆序排列。返回 None 。
3.2 新版功能.
rotate(n=1) 向右循环移动 n 步。 如果 n 是负数,就向左循环。
如果deque不是空的,向右循环移动一步就等价于 d.appendleft(d.pop()) , 向左循环一步就等价于 d.append(d.popleft()) 。
Deque对象同样提供了一个只读属性:
maxlen Deque的最大尺寸,如果没有限定的话就是 None 。
3.1 新版功能.
除了以上操作,deque 还支持迭代、封存、len(d)、reversed(d)、copy.copy(d)、copy.deepcopy(d)、成员检测运算符 in 以及下标引用例如通过 d[0] 访问首个元素等。 索引访问在两端的复杂度均为 O(1) 但在中间则会低至 O(n)。 如需快速随机访问,请改用列表。
更多示例见代码示例文件: code/datastructures/python_collections.py
4.1.3collections.ChainMap 对象
class collections.ChainMap(*maps)
ChainMap 是 Python 3.3 新版功能
一个 ChainMap 类是为了将多个映射快速的链接到一起,这样它们就可以作为一个单元处理。它通常比创建一个新字典和多次调用 update() 要快很多。
这个类可以用于模拟嵌套作用域,并且在模版化的时候比较有用。
一个 ChainMap 将多个字典或者其他映射组合在一起,创建一个单独的可更新的视图。 如果没有 maps 被指定,就提供一个默认的空字典,这样一个新链至少有一个映射。
底层映射被存储在一个列表中。这个列表是公开的,可以通过 maps 属性存取和更新。没有其他的状态。
搜索查询底层映射,直到一个键被找到。不同的是,写,更新和删除只操作第一个映射。
一个 ChainMap 通过引用合并底层映射。 所以,如果一个底层映射更新了,这些更改会反映到 ChainMap 。
支持所有常用字典方法。另外还有一个 maps 属性(attribute),一个创建子上下文的方法(method), 一个存取它们首个映射的属性(property):
-
maps
一个可以更新的映射列表。这个列表是按照第一次搜索到最后一次搜索的顺序组织的。它是仅有的存储状态,可以被修改。列表最少包含一个映射。
-
new_child
(m=None)返回一个新的 ChainMap 类,包含了一个新映射(map),后面跟随当前实例的全部映射(map)。如果
m
被指定,它就成为不同新的实例,就是在所有映射前加上 m,如果没有指定,就加上一个空字典,这样的话一个d.new_child()
调用等价于ChainMap({}, *d.maps)
。这个方法用于创建子上下文,不改变任何父映射的值。在 3.4 版更改: 添加了m
可选参数。 -
parents
属性返回一个新的 ChainMap 包含所有的当前实例的映射,除了第一个。这样可以在搜索的时候跳过第一个映射。 使用的场景类似在 nested scopes 嵌套作用域中使用 nonlocal 关键词。用例也可以类比内建函数 super() 。一个
d.parents
的引用等价于ChainMap(*d.maps[1:])
。
注意,一个 ChainMap() 的迭代顺序是通过扫描最后的映射来确定的:
from collections import ChainMap
baseline = {'music': 'bach', 'art': 'rembrandt'}
adjustments = {'art': 'van gogh', 'opera': 'carmen'}
list(ChainMap(adjustments, baseline))
# ['music', 'art', 'opera']
这给出了与 dict.update() 调用序列相同的顺序,从最后一个映射开始:
baseline = {'music': 'bach', 'art': 'rembrandt'}
adjustments = {'art': 'van gogh', 'opera': 'carmen'}
combined = baseline.copy()
combined.update(adjustments)
print(list(combined))
# ['music', 'art', 'opera']
在 3.9 版更改: 增加了对 |
和 |=
运算符的支持,相关说明见 PEP 584。
4.1.4collections.Counter 字典的子类
class collections.Counter([iterable-or-mapping])
一个 Counter 是一个 dict 的子类,用于计数可哈希对象。它是一个集合,元素像字典键(key)一样存储,它们的计数存储为值。计数可以是任何整数值,包括0和负数。 Counter 类有点像其他语言中的 bags或multisets。
元素从一个 iterable 被计数或从其他的 mapping (or counter)初始化:
from collections import Counter
c = Counter() # a new, empty counter
c = Counter('gallahad') # a new counter from an iterable
c = Counter({'red': 4, 'blue': 2}) # a new counter from a mapping
c = Counter(cats=4, dogs=8) # a new counter from keyword args
Counter对象有一个字典接口,如果引用的键没有任何记录,就返回一个0,而不是弹出一个 KeyError :
from collections import Counter
c = Counter(['eggs', 'ham'])
print(c['bacon']) # count of a missing element is zero
# 0
设置一个计数为0不会从计数器中移去一个元素。使用 del
来删除它:
from collections import Counter
c = Counter(['eggs', 'ham'])
c['sausage'] = 0 # counter entry with a zero count
del c['sausage'] # del actually removes the entry
Python 3.1 新版功能.
在 3.7 版更改: 作为 dict 的子类,Counter 继承了记住插入顺序的功能。 Counter 对象进行数学运算时同样会保持顺序。 结果会先按每个元素在运算符左边的出现时间排序,然后再按其在运算符右边的出现时间排序。
计数器对象除了字典方法以外,还提供了三个其他的方法:
通常字典方法都可用于 Counter 对象,除了有两个方法工作方式与字典并不相同。
-
fromkeys
(iterable)这个类方法没有在 Counter 中实现。
-
update
([iterable-or-mapping])从 迭代对象 计数元素或者 从另一个 映射对象 (或计数器) 添加。 像 dict.update() 但是是加上,而不是替换。另外,迭代对象 应该是序列元素,而不是一个
(key, value)
对。
4.1.5collections.OrderedDict 对象
有序词典就像常规词典一样,但有一些与排序操作相关的额外功能。由于内置的 dict 类获得了记住插入顺序的能力(在 Python 3.7 中保证了这种新行为),它们变得不那么重要了。
一些与 dict 的不同仍然存在:
- 常规的 dict 被设计为非常擅长映射操作。 跟踪插入顺序是次要的。
- OrderedDict 旨在擅长重新排序操作。 空间效率、迭代速度和更新操作的性能是次要的。
- 算法上, OrderedDict 可以比 dict 更好地处理频繁的重新排序操作。 这使其适用于跟踪最近的访问(例如在 LRU cache 中)。
- 对于 OrderedDict ,相等操作检查匹配顺序。
- OrderedDict 类的
popitem()
方法有不同的签名。它接受一个可选参数来指定弹出哪个元素。 - OrderedDict 类有一个
move_to_end()
方法,可以有效地将元素移动到任一端。 - Python 3.8之前, dict 缺少 __reversed__() 方法。
class collections.``OrderedDict
([items])
返回一个 dict 子类的实例,它具有专门用于重新排列字典顺序的方法。3.1 新版功能.popitem
(last=True)有序字典的 popitem() 方法移除并返回一个 (key, value) 键值对。 如果 last 值为真,则按 LIFO 后进先出的顺序返回键值对,否则就按 FIFO 先进先出的顺序返回键值对。move_to_end
(key, last=True)将现有 key 移动到有序字典的任一端。 如果 last 为真值(默认)则将元素移至末尾;如果 last 为假值则将元素移至开头。如果 key 不存在则会触发 KeyError:>>>>>> d = OrderedDict.fromkeys('abcde') >>> d.move_to_end('b') >>> ''.join(d.keys()) 'acdeb' >>> d.move_to_end('b', last=False) >>> ''.join(d.keys()) 'bacde'
3.2 新版功能.
相对于通常的映射方法,有序字典还另外提供了逆序迭代的支持,通过 reversed() 。
OrderedDict 之间的相等测试是顺序敏感的,实现为 list(od1.items())==list(od2.items())
。 OrderedDict 对象和其他的 Mapping 的相等测试,是顺序敏感的字典测试。这允许 OrderedDict 替换为任何字典可以使用的场所。
在 3.5 版更改: OrderedDict 的项(item),键(key)和值(value) 视图 现在支持逆序迭代,通过 reversed() 。
在 3.6 版更改: PEP 468 赞成将关键词参数的顺序保留, 通过传递给 OrderedDict 构造器和它的 update()
方法。
在 3.9 版更改: 增加了合并 (|
) 与更新 (|=
) 运算符,相关说明见 PEP 584。
4.1.6defaultdict 对象
class collections.``defaultdict
([default_factory[, …]])
返回一个新的类似字典的对象。 defaultdict 是内置 dict 类的子类。它重载了一个方法并添加了一个可写的实例变量。其余的功能与 dict 类相同,此处不再重复说明。
本对象包含一个名为 default_factory 的属性,构造时,第一个参数用于为该属性提供初始值,默认为 None
。所有其他参数(包括关键字参数)都相当于传递给 dict 的构造函数。
defaultdict 对象除了支持标准 dict 的操作,还支持以下方法作为扩展:
-
__missing__
(key)如果 default_factory 属性为
None
,则调用本方法会抛出 KeyError 异常,附带参数 key。如果 default_factory 不为None
,则它会被(不带参数地)调用来为 key 提供一个默认值,这个值和 key 作为一对键值对被插入到字典中,并作为本方法的返回值返回。如果调用 default_factory 时抛出了异常,这个异常会原封不动地向外层传递。在无法找到所需键值时,本方法会被 dict 中的 __getitem__() 方法调用。无论本方法返回了值还是抛出了异常,都会被 __getitem__() 传递。注意,__missing__() 不会 被 __getitem__() 以外的其他方法调用。意味着get()
会像正常的 dict 那样返回None
,而不是使用 default_factory。
defaultdict 对象支持以下实例变量:
-
default_factory
本属性由 __missing__() 方法来调用。如果构造对象时提供了第一个参数,则本属性会被初始化成那个参数,如果未提供第一个参数,则本属性为
None
。
在 3.9 版更改: 增加了合并 (|
) 与更新 (|=
) 运算符,相关说明见 PEP 584。
4.1.7UserDict 对象
UserDict 类是用作字典对象的外包装。对这个类的需求已部分由直接创建 dict 的子类的功能所替代;不过,这个类处理起来更容易,因为底层的字典可以作为属性来访问。
-
class
collections.``UserDict
([initialdata])模拟一个字典类。这个实例的内容保存为一个正常字典, 可以通过 UserDict 实例的 data 属性存取。如果提供了 initialdata 值, data 就被初始化为它的内容;注意一个 initialdata 的引用不会被保留作为其他用途。UserDict 实例提供了以下属性作为扩展方法和操作的支持:
data
一个真实的字典,用于保存 UserDict 类的内容。
4.1.8UserList 对象
这个类封装了列表对象。它是一个有用的基础类,对于你想自定义的类似列表的类,可以继承和覆盖现有的方法,也可以添加新的方法。这样我们可以对列表添加新的行为。
对这个类的需求已部分由直接创建 list 的子类的功能所替代;不过,这个类处理起来更容易,因为底层的列表可以作为属性来访问。
-
class
collections.``UserList
([list])模拟一个列表。这个实例的内容被保存为一个正常列表,通过 UserList 的 data 属性存取。实例内容被初始化为一个 list 的copy,默认为
[]
空列表。 list 可以是迭代对象,比如一个Python列表,或者一个 UserList 对象。UserList 提供了以下属性作为可变序列的方法和操作的扩展:data
一个 list 对象用于存储 UserList 的内容。
子类化的要求: UserList 的子类需要提供一个构造器,可以无参数调用,或者一个参数调用。返回一个新序列的列表操作需要创建一个实现类的实例。它假定了构造器可以以一个参数进行调用,这个参数是一个序列对象,作为数据源。
如果一个分离的类不希望依照这个需求,所有的特殊方法就必须重写;请参照源代码进行修改。
4.1.9UserString 对象
UserString 类是用作字符串对象的外包装。对这个类的需求已部分由直接创建 str 的子类的功能所替代;不过,这个类处理起来更容易,因为底层的字符串可以作为属性来访问。
-
class
collections.``UserString
(seq)模拟一个字符串对象。这个实例对象的内容保存为一个正常字符串,通过 UserString 的 data 属性存取。实例内容初始化设置为 seq 的copy。seq 参数可以是任何可通过内建 str() 函数转换为字符串的对象。UserString 提供了以下属性作为字符串方法和操作的额外支持:
data
一个真正的 str 对象用来存放 UserString 类的内容。在 3.5 版更改: 新方法__getnewargs__
,__rmod__
,casefold
,format_map
,isprintable
, 和maketrans
。