参考
-
《简明python教程》
Note: 更多连载请查看【python】
最近一次修订时间为 2020-11-04
图片来源:https://www.bilibili.com/video/av20982396/?p=8
字典是一些列键(key)值(value)对组成的,形式如下:{键1:值1,键2:值2}
每个键值对用冒号隔开,每对之间用逗号隔开
1 字典的创建
1.1 用大括号{}创建
dict1 = {'Bob':18,'Alice':17}#创建字典
print (dict1)
type(dict1)
ouput
{'Bob': 18, 'Alice': 17}
dict
键必须是唯一的,必须是不可变的,如字符串,数字,元组,值可以是任何数据类型
dict2 = {'慧慧':[1,'a',[2.33,'Python']],1:('Hello World!')}
dict2
output
{1: 'Hello World!', '慧慧': [1, 'a', [2.33, 'Python']]}
可以看出,字典的value可以是元组,也可以是列表
1.2 创建空字典
dict3 = {}#创建空字典
print (dict3)
type (dict3)
output
{}
dict
1.3 用 dict() 创建(不推荐)
除了直接用大括号创建字典,还可以用dict()来创建字典(不推荐)
用法如下:
-
通过放入列表套元组或者元组套列表实现
-
或者列表套列表 、元组套元组,还记得在元组里讲到的列表和元组的相互转换么?
1.3.1 列表套元组
dict4 = dict([('Bob',18),('Alice',17)]) # 除了直接用大括号创建字典,还可以用dict()来创建字典
dict4
output
{'Alice': 17, 'Bob': 18}
1.3.2 元组套列表
dict5 = dict((['Bob',18],['Alice',17]))
dict5
结果同上
1.3.3 元组套元组
dict6 = dict((('Bob',18),('Alice',17)))
结果同上
1.3.4 列表套列表
dict7 = dict([['Bob',18],['Alice',17]])
结果同上
补充:下边这些代码,他们都在执行一样的操作吗?你看得出差别吗?
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
output
True
1.4 借助 dict() 和等号来创建
这种创建字典情况下,键只能为字符串类型,并且创建的时候字符串不用加引号
dict8 = dict(Bob = 18 , Alice = 17)#这种创建字典情况下,键只能为字符串类型,并且创建的时候字符串不用加引号
dict8
结果同上
Note1:如果加了引号,会报错
SyntaxError: keyword can’t be an expression
Note2:用这种方法不能创建键为数值型的字典
SyntaxError: keyword can’t be an expression
用如下方式则可以
dict9 = {1:18 , 2:17}
dict9
output
{1: 18, 2: 17}
2 访问字典中的值
上面是根据 keys 来访问 values,下面试试根据 values 来访问 keys
dict1 = {"a":1,"b":2,"c":3}
print(dict1.keys())
print(dict1.values())
output
dict_keys(['a', 'b', 'c'])
dict_values([1, 2, 3])
实现下访问功能
def reverse_lookup(dict1,value1):
for i in dict1.keys():
if dict1[i] == value1:
return i
print("no value")
测试下效果
reverse_lookup(dict1,2)
output
'b'
再试试,没有的 values
reverse_lookup(dict1,4)
output
no value
3 添加字典的键值对
dict1 = {'Alice': 17, 'Bob': 18}
dict1['Tom'] = 19 #添加键值对
print (dict1)
output
{'Alice': 17, 'Bob': 18, 'Tom': 19}
4 修改字典的键值对
dict1 = {'Alice': 17, 'Bob': 18}
dict1['Alice'] = 18 #更新修改键值对
print (dict1)
output
{'Alice': 18, 'Bob': 18}
5 删除字典的键值对
dict1 = {'Alice': 17, 'Bob': 18}
del dict1['Bob'] #删除键值对
print (dict1)
output
{'Alice': 17}
6 字典的遍历
dict1 = {'Alice': 18, 'Tom': 19}
for key in dict1.keys():#遍历字典只需要遍历它的键
print(key,dict1[key])
output
Alice 18
Tom 19
或者直接
dict1 = {'Alice': 18, 'Tom': 19}
for i in dict1:
print(dict1[i])
output
18
19
7 字典的复制
复制分为潜复制和深复制,浅复制是复制品A1会随着被复制品A的改变而改变,深复制是复制品A1不会随着A的改变而变动,复制以后独立了。我们常常理解的复制对应着深复制。
7.1 浅复制
下面看一下浅复制
dict1 = {'Bob': None, 'Tom': 19}
dict2 = dict1
print (dict2)
dict1['Bob'] = 19
print(dict1)
print(dict2)
output
{'Bob': None, 'Tom': 19}
{'Bob': 19, 'Tom': 19}
{'Bob': 19, 'Tom': 19}
可以看出,dict2随着dict1的改变而改变
我们来看看他们的内存地址
print (id(dict1))
print (id(dict2))
output
140113040589088
140113040589088
结果表明,dict1和dict2虽然是两个变量,但是指向着同一块内存空间
7.2 深复制
深拷贝可以用引入 copy
模块实现
import copy#深拷贝可以用引入copy模块实现
dict1 = {'Bob': None, 'Tom': 19}
dict2 = copy.deepcopy(dict1)
print(dict2)
dict1['Bob'] = 19
print(dict2)
dict1.clear()
print(dict1)
print(dict2)
output
{'Bob': None, 'Tom': 19}
{'Bob': None, 'Tom': 19}
{}
{'Bob': None, 'Tom': 19}
结果表明,dict2不会随着dict1的改变而改变,是深度复制,看看他们的内存地址
print(id(dict1))
print(id(dict2))
output
140113040575512
140113040589952
dict1 和 dict2 是内存地址不同的两个变量
8 字典的一些基本方法
8.1 keys()
返回包含字典所有key的列表
dict1 = {'Alice': 17, 'Bob': 18}
dict1.keys()#返回包含字典所有key的列表
output
dict_keys(['Alice', 'Bob'])
8.2 values()
返回包含字典所有value的列表
dict1 = {'Alice': 17, 'Bob': 18}
dict1.values()#返回包含字典所有value的列表
output
dict_values([17, 18])
8.3 items()
返回包含所有(键,值)项的列表
dict1 = {'Alice': 17, 'Bob': 18}
dict1.items()#返回包含所有(键,值)项的列表
output
dict_items([('Alice', 17), ('Bob', 18)])
8.4 clear()
删除字典中的所有项或元素,无返回值 (注意,不是删除字典,而是清空字典内容)
dict2 = {'乐子': [1, 'a', [2.33, 'Python']], 1: 'Hello World!'}
dict2.clear()#删除字典中的所有项或元素,无返回值(注意,不是删除字典,而是清空字典内容)
dict2
output
{}
8.5 get()
返回字典中key对应的值,若key不存在,则返回 default 的值(default 不设定默认为 None),区别于第二节访问字典中的值dict[key] 的方法,dict[key] 当key不存在时会报错,而get()是返回 default 的值
dict1 = {'Alice': 18, 'Tom': 19}
print (dict1.get('Alice'))#返回字典中key对应的值,若key不存在,则返回default的值(default默认为None)
print (dict1['Alice'])
print (dict1.get('Bob'))
output
18
18
None
得益于此,你的代码不会中断
dict1 = {'Alice': 18, 'Tom':19}
print(dict1.get('Bob', 20)) # 如果键不在字典中返回默认值 None 或者设定的默认值
print(dict1)
output
20
{'Alice': 18, 'Tom': 19}
也可以利用 get 来赋值
dit = {}
dit["1"] = dit.get("1", 1)
print(dit)
output
{'1': 1}
8.6 pop()
如果字典中存在key,则删除并返回 key 对应的 value;如果key不存在,且没有给出default值,则引发KeyError异常
dict1 = {'Alice': 18, 'Tom': 19}
a = dict1.pop('Alice')#如果字典中存在key,则删除并返回key对应的value;如果key不存在,且没有给出default值,则引发KeyError异常
print (a)
print (dict1)
output
18
{'Tom': 19}
Python 字典 popitem() 方法随机返回并删除字典中的一对键和值。
如果字典已经为空,却调用了此方法,就报出KeyError异常。
Python 字典 popitem() 方法
dict1 = {'Alice': 18, 'Tom': 19}
a = dict1.popitem()
print (a)
print(dict1)
output
('Tom', 19)
{'Alice': 18}
8.7 setdefault()
如果字典不存在key,则由dict[key] = default为其赋值
dict1 = {'Alice': 18, 'Tom': 19}
dict1.setdefault('Bob')#如果字典不存在key,则由dict[key] = default为其赋值
dict1
output
{'Alice': 18, 'Bob': None, 'Tom': 19}
8.8 update()
dict1.update(dict2) 是将字典dict2中键值对添加到dict1中
dict1 = {'Alice': 18, 'Tom': 19}
dict2 = {'a':1,'b':2}
dict1.update(dict2)#将字典dict2中键值对添加到dict1中
dict1
output
{'Alice': 18, 'Tom': 19, 'a': 1, 'b': 2}
9 dict 的存储方式
[扩展阅读] 你知道 Python 的字典(Dict)是如何存储的吗?
Python 的字典有好多个名称(“映射”、“哈希”、“散列"或者"关系数组”),那你知道为什么字典会被称为 Hash(翻译为"哈希"或"散列")吗?
你知道为什么字典对于键(Key)的存储数据要求比较严格,但对于对应的值(Value)的存储却要求很宽松吗?
读完这篇文章,你将深刻理解这些问题背后的真相!
首先我们来解释一下什么是 Hash,来自维基百科:
散列函数(或散列算法,又称哈希函数,英语:Hash Function)是一种从任何一种数据中创建小的数字“指纹”的方法。散列函数把消息或数据压缩成摘要,使得数据量变小,将数据的格式固定下来。该函数将数据打乱混合,重新创建一个叫做散列值的指纹。散列值通常用来代表一个短的随机字母和数字组成的字符串。好的散列函数在输入域中很少出现散列冲突。在散列表和数据处理中,不抑制冲突来区别数据,会使得数据库记录更难找到。
世界上没有两片完全相同的树叶,也没有两个相同的指纹,散列函数是用于从数据中创建小的数字指纹的方法。
我们看下图示:

如图,Python 调用内部的散列函数,将键(Key)作为参数进行转换,得到一个唯一的地址(这也就解释了为什么给相同的键赋值会直接覆盖的原因,因为相同的键转换后的地址是一样滴),然后将值(Value)存放到该地址中。
对于 Python 来说,键(Key)必须是可哈希的,换句话说就是要可以通过散列函数计算出唯一地址的。那如果拿一个变量当键(Key)可以吗?肯定不行。因为变量随时都可能改变,不符合可哈希原则!
同样的,列表、字典、集合这些都是可变的,所以都不能做为键(Key)来使用。
那有朋友可能会问,那元组呢?元组总该是不变的吧?
其实不然,因为元组里边可以存放列表这类可变因素,所以如果实在想拿元组当字典的键(Key),那必须对元组做限制:元组中只包括像数字和字符串这样的不可变元素时,才可以作为字典中有效的键(Key)。
另外还需要注意的一点是,Python 的哈希算法对相同的值计算得到的结果是一样的,也就是说 12315 和 12315.0 的值相同,他们被认为是相同的键(Key)。
来自 https://stackoverflow.com/questions/32209155/why-can-a-floating-point-dictionary-key-overwrite-an-integer-key-with-the-same-v/32211042#32211042
1 == 1.0
hash(1) == hash(1.0)
output
True
True
Q1: 成员资格操作符(in 和 not in)可以检查一个元素是否存在序列中,当然也可以用来检查一个键(Key)是否存在字典中,那么请问哪种的检查效率更高些?为什么?
答:在字典中检查键(Key)是否存在比在序列中检查指定元素是否存在更高效。因为字典的原理是使用哈希算法存储,一步到位,不需要使用查找算法进行匹配,因此时间复杂度是O(1),效率非常高。(关于如何使用哈希算法存储的具体原理可以参考第3题的“扩展阅读”部分)
Q2:Python对键(Key)和值(Value)有没有类型限制?
答:Python对键的要求相对要严格一些,要求它们必须是可哈希(Hash)的对象,不能是可变类型(包括变量、列表、字典本身等)。但是Python对值是没有任何限制的,它们可以是任意的Python对象。
附录
合并两个字典({**a,**b})
dict1 = {1:"1",2:"2"}
dict2 = {3:"3",4:"4"}
dict1.update(dict2)
print(dict1)
output
{1: '1', 2: '2', 3: '3', 4: '4'}
或者用更简洁的方式
dict1 = {1:"1",2:"2"}
dict2 = {3:"3",4:"4"}
print({**dict1,**dict2})
output
{1: '1', 2: '2', 3: '3', 4: '4'}
简单的一个 |
也可以用于字典合并
dict1 = {1:"1",2:"2", 3:"3",}
dict2 = {3:"3",4:"4"}
dict3 = dict1 | dict2
print(dict3)
output
{1: '1', 2: '2', 3: '3', 4: '4'}
**dict
来自 Python 奇淫技巧!
双星“**”放在字典的前面可以让你将字典的内容作为命名参数传递给函数。字典的键是参数的名字,键的值作为参数的值传递给函数
dict1 = {"a":1, "b":2}
def f1(a, b):
print(a+b)
return
f1(**dict1)
f1(a=1, b=2)
output
3
3
将两个列表转化为字典(zip)
先介绍下 zip 函数
zip()
函数名取自拉链zipper~
zip()函数接收两个或者多个序列,返回一个元组列表,每个元组包含来自每个序列中的一个元素
参考
先看一个简单的例子
list1 = [1, 2, 3, 4, 5]
tuple1 = (6, 7, 8, 9, 10)
s1 = "dream"
for item in zip(list1,tuple1,s1):
print(item)
output
(1, 6, 'd')
(2, 7, 'r')
(3, 8, 'e')
(4, 9, 'a')
(5, 10, 'm')
再看看如何把两个列表变成字典
list1 = [1,2,3,4]
list2 = ["1","2","3","4"]
print(dict(zip(list1,list2)))
output
{1: '1', 2: '2', 3: '3', 4: '4'}
这里顺便介绍下 *
,相当于解压操作
print(divmod(9, 4)) # 返回商和余数
output
(2, 1)
tuple1 = (9,4)
print(divmod(tuple1))
这样会报错
TypeError: divmod expected 2 arguments, got 1
用 * 解压一下就行
print(divmod(*tuple1))
output
(2, 1)
再看一个小例子
list1 = ["123","456"]
for i in zip(*list1):
print(i)
('1', '4')
('2', '5')
('3', '6')
不使用 if-else 的 operator
import operator
action = {
"+":operator.add,
"-":operator.sub,
"/":operator.truediv,
"*":operator.mul,
"**":pow
}
print(action["*"](2,3))
output
6
字典的排序
Python进阶系列连载(2)——那些容易被忽略的问题(中)
先看看对按 key 来排序
dict1 = {"f": 6, "a": 12, "b": 34, "d": 56}
for key in sorted(dict1):
print(key,dict1[key])
output
a 12
b 34
d 56
f 6
更灵活的写法(来自 Python字典:竟还有我不会的高阶玩法?)
dic = {'a': 2, 'b': 1, 'c': 3, 'd': 0}
lst1 = sorted(dic.items(), key=lambda x: x[0], reverse=False)
# [('a', 2), ('b', 1), ('c', 3), ('d', 0)]
lst2 = sorted(dic.items(), key=lambda x: x[1], reverse=False)
# [('d', 0), ('b', 1), ('a', 2), ('c', 3)]
print('按照键:', {key: value for key, value in lst1})
print('按照值:', {key: value for key, value in lst2})
output
按照键: {'a': 2, 'b': 1, 'c': 3, 'd': 0}
按照值: {'d': 0, 'b': 1, 'a': 2, 'c': 3}
多个字典排序
dict_list = [
{'letter': 'B', 'number': '2'},
{'letter': 'A', 'number': '3'},
{'letter': 'B', 'number': '1'}
]
# 按 letter 排序
print(sorted(dict_list,
key=lambda dic: dic['letter']))
# 按 letter, number 排序
print(sorted(dict_list,
key=lambda dic: (dic['letter'], dic['number'])))
output
[{'letter': 'A', 'number': '3'}, {'letter': 'B', 'number': '2'}, {'letter': 'B', 'number': '1'}]
[{'letter': 'A', 'number': '3'}, {'letter': 'B', 'number': '1'}, {'letter': 'B', 'number': '2'}]
统计字典中元素出现的频率
借助 collections
库中的 Counter
方法
list1 = [1, 2, 3, 4, 5, 6, 8, 1, 2, 3, 6, 9, 8, 6]
from collections import Counter
print(type(Counter(list1)))
print(Counter(list1))
print(dict(Counter(list1)))
output
<class 'collections.Counter'>
Counter({6: 3, 1: 2, 2: 2, 3: 2, 8: 2, 4: 1, 5: 1, 9: 1})
{1: 2, 2: 2, 3: 2, 4: 1, 5: 1, 6: 3, 8: 2, 9: 1}
defaultdict 建立特定属性的字典
往字典 key 中添加元素或者赋值,需要先判定 key 是否在字典中
我们可以借助 defaultdict
方法,来初始化字典的 value
from collections import defaultdict
dict1 = defaultdict(int)
dict2 = defaultdict(str)
dict3 = defaultdict(list)
dict4 = defaultdict(tuple)
dict5 = defaultdict(dict)
dict6 = defaultdict(set)
dict7 = defaultdict(bool)
for i in range(1, 8):
print("dict%s:" % str(i))
print(type(eval("dict%s" % str(i)))) # 等价于 type(dict1)
print(eval("dict%s[0]" % str(i))) # 等价于 dict1[0]
output
dict1:
<class 'collections.defaultdict'>
0
dict2:
<class 'collections.defaultdict'>
dict3:
<class 'collections.defaultdict'>
[]
dict4:
<class 'collections.defaultdict'>
()
dict5:
<class 'collections.defaultdict'>
{}
dict6:
<class 'collections.defaultdict'>
set()
dict7:
<class 'collections.defaultdict'>
False
可以看到 dict1[0]
会根据自己设定的 value 模式初始化,不会报错
下面看一下更灵活的例子,例如把相同 key 的合并到一起,value 是list
from collections import defaultdict
list1 = [("p", 1), ("p", 2), ("p", 3),
("q", 4), ("q", 5), ("q", 6)]
# 常规操作
dict1 = {}
for key, value in list1:
if key in dict1:
dict1[key].append(value)
else:
dict1[key] = [value]
print("dict1:\n", dict1)
# 借助 defaultdict
dict2 = defaultdict(list)
for key, value in list1:
dict2[key].append(value)
print("dict2:\n", dict2)
print("the value of p key:\n", dict2["p"])
output
dict1:
{'p': [1, 2, 3], 'q': [4, 5, 6]}
dict2:
defaultdict(<class 'list'>, {'p': [1, 2, 3], 'q': [4, 5, 6]})
the value of p key:
[1, 2, 3]
EasyDict
EasyDict 可以让你像访问属性一样访问 dict 里的变量
from easydict import EasyDict
train = EasyDict(
batch_size=512,
epoch_num=160,
workers=8,
seed=20201223,
)
print(train["batch_size"])
print(train.batch_size)
output
512
512
可以把配置信息都写成 EasyDict 的形式,保存在 config.py 中,然后调用时
import config as cfg
cfg.train.batch_size
推导式
d = { x: x % 2 == 0 for x in range(1, 11) }
print(d)
print(type(d))
output
{1: False, 2: True, 3: False, 4: True, 5: False, 6: True, 7: False, 8: True, 9: False, 10: True}
<class 'dict'>
集合运算:字典 key 的并集, 字典 key 的并集
dic1 = {'Python': 1, 'Java': 2, 'C': 3}
dic2 = {'Python': 3, 'Java': 2, 'C++': 1}
print({k: dic1[k] for k in dic1.keys() & dic2.keys()})
print(dic1.items() & dic2.items())
output
{'Java': 2, 'Python': 1}
{('Java', 2)}
交换字典的键和值
dictionary = {"a": 1, "b": 2, "c": 3}
reversed_dictionary = {j: i for i, j in dictionary.items()}
print(reversed_dictionary) # {1: 'a', 2: 'b', 3: 'c'}
Note: 更多连载请查看【python】