字典

什么是字典

字典并不是什么全新的概念,早在上小学时,你就已经接触过字典,没错,就是《新华字典》,新华字典的结构和python语言中的字典,在结构上是一样的。
咱们以读音来查找一个汉字,比如”张”这个字,读音是zhang,一声,你一定可以在字典里找到与它对应的页数,假设是第100页,那么zhang 和 100之间就有一个映射关系,你知道了zhang,也就知道了100。
另一个较为常见的例子是手机通讯录,你想找一个人的电话时,你应该在通讯录里找到这个人的名字,然后点进去查看它的电话号,姓名和电话号之间存在着映射关系,你知道姓名,就知道电话号,下面这张图展示了字典的数据结构
Python字典 - 图1
左侧是key,右侧是value。

python字典 (dict)

python中的字典(dict)是数据的无序集合, 与列表,元组,集合将单个值作为存储数据不同, 字典存储的是key:value对, 字典中存储的数据总是成对的, key被称之为键, value称之为值, 值可以是任意数据且可重复, 而键不可重复且必须是可hash的。

1. 创建一个字典

1.1 创建字典

字典是一个容器类型数据,字典里存储的是键值对,键值对用冒号分割key与value,每个键值(key-value)对用逗号分割,整个字典包裹在花括号{}中。
下面是一个字典的示例

  1. contacts_dict = {
  2. "小王": '13892339876',
  3. "小张": '13898320987',
  4. "小李": '13890348745'
  5. }

如果字典里没有任何键值对,那么它就是一个空字典

  1. empty_dict = {}

如果想知道字典中有多少数据,可以使用len函数来获取

  1. print(len(contacts_dict))

想要知道一个key是否在字典中存在,使用in 或者not in 成员操作符

  1. print('小王' in contacts_dict)

1.2 什么数据可以做key,什么数据可以做value

什么数据可以做key
并不是所有的数据都可以做key,想成为key,是有要求的,数据必须是可hash的,下面罗列的5种数据类型是可以做字典key的数据类型

  1. bool
  2. int
  3. float
  4. 字符串
  5. 元组

下面3中数据类型不可以做字典的key

  1. 列表
  2. 集合
  3. 字典

凡是可变对象都不可以做字典的key,凡是不可变对象,都可以做字典的key。
bool类型的数据只有True和False两个值,虽然他们可以做字典的key,但实践时,你最好不要这样做,会导致古怪的问题,比如下面的代码

  1. int_dict = {
  2. 1: '1做key',
  3. True: 'True做key'
  4. }
  5. print(int_dict)

执行代码,输出结果为

  1. {1: 'True做key'}

是不是很奇怪,明明两个键值对,实际输出却只有一个,而且key从True变成了1,这是怎么一回事?

  1. print(1==True) # 判断1是否与True相等
  2. print(issubclass(bool, int)) # 判断bool类型是否为int类型的子类

程序输出结果为

  1. True
  2. True

1 和 True是相等的,0和False是相等的,bool类型是int类型的子类,这就是原因,在定义字典时,第二个键值对覆盖了前面的键值对,在字典中,key是不会出现重复的,后加入的总是会覆盖前面的。
什么数据可以做value
对于value,字典没有任何要求,任何数据都可以做value,包括列表,集合,甚至是字典。

  1. dic = {
  2. 'list': [2, 3, 4],
  3. 'set': set([2, 4, 6]),
  4. 'dict': {
  5. '小明': 99
  6. }
  7. }

2. 新增键值对和修改value

下面的字典里存储了学生的语文考试分数

  1. score_dict = {
  2. '小明': 96,
  3. '小刚': 98,
  4. '小红': 94
  5. }

现在你发现,小丽的分数还没有写入到字典中,她考了100分,你需要新增一个键值对,记录小丽的分数

  1. score_dict['小丽'] = 100

这就是字典里新增键值对的方式,一对中括号紧跟在字典后面,中括号里写key,等号右侧写value,是不是很简单。
如果一个key原本已经存在了,那么上面这种操作方式会产生什么样的结果呢?

  1. score_dict['小明'] = 99

‘小明’这个key已经存在于字典中了,上面这种写法尝试新增,但不会发生新增这种事情,因为字典里的key不允许重复,因此,最终的结果是value被修改,字典被修改成

  1. score_dict = {
  2. '小明': 99,
  3. '小刚': 98,
  4. '小红': 94
  5. }

简单总结一下,对于score_dict[‘小明’] = 99 这种代码,如果key不存在,那么就会新增键值对,如果key已经存在,就会修改value

3. 访问字典里的值

想要访问字典里的值,必须提供key,对字典的任何操作,都必须通过key才能进行,就如同对列表的操作都必须通过索引才能进行一样。
比如你想知道小红的语文分数,那你应该这样写代码

  1. score_dict = {
  2. '小明': 99,
  3. '小刚': 98,
  4. '小红': 94
  5. }
  6. print(score_dict['小红'])

score_dict[‘小红’] 这种写法的意思就是获取’小红’这个key所对应的value,如果你提供了一个并不存在的key,则会引发一个KeyError错误

  1. print(score_dict['小丽'])

错误为

  1. Traceback (most recent call last):
  2. File "/Users/kwsy/PycharmProjects/pythonclass/mytest/demo.py", line 7, in <module>
  3. print(score_dict['小丽'])
  4. KeyError: '小丽'

4. 删除字典里的键值对

现在想要从字典里删除小红的成绩,和访问字典里的key一样,你必须指定要删除的key

  1. del score_dict['小红']

除了这种方法外,你还可以使用字典的pop方法

  1. score_dict.pop('小红')

python嵌套字典

同嵌套列表一样,python的字典作为一个容器,可以在字典里存储字典,类似下面的形式

  1. stu_dict = {
  2. 'name': '小明',
  3. 'age': 12,
  4. 'score': {
  5. '语文': 90,
  6. '数学': 98
  7. }
  8. }

在嵌套列表,嵌套字典中,对于嵌套的层次,没有任何要求,只要你自己能理清嵌套逻辑,你想嵌套多少层都可以。
如果你想获取小明的语文分数,那么就需要逐层的来获取value

  1. print(stu_dict['score']['语文'])

stu_dict[‘score’]的值是

  1. {
  2. '语文': 90,
  3. '数学': 98
  4. }

仍然是一个字典,想要获取语文分数,就继续使用[]操作符来获取key所对应的value

python字典方法介绍

方法 功能作用
clear() 删除字典内所有的元素
copy() 返回字典的浅复制
fromkeys() 以指定key创建一个新的字典
get() 返回指定key的值
items() 成对返回所有key和value
keys() 返回字典所有的key
values 返回字典所有value
setdefault() 为key设置对应的默认值
update() 更新字典
pop() 删除键值对

字典内置的方法可以帮我们实现很多功能,下面逐个介绍他们,讲解他们的用途

1. clear, 删除字典内所有的元素

  1. dic = {
  2. '小明': 98
  3. }
  4. dic.clear()
  5. print(dic)

使用clear方法后,字典dic变成了空字典,有人可能会问,这种清空字典的方法和直接将空字典赋值给变量dic有什么区别

  1. dic = {
  2. '小明': 98
  3. }
  4. dic = {}
  5. print(dic)

程序最终输出的结果同样是{},dic 变成了空字典。两种方式,变量dic都变成了空字典,但意义不同,使用clear方法,字典在内存中的地址没有发生变化,但是第二种方法,变量dic指向了一个新的空字典,原来的字典被垃圾回收机制回收了,我们可以通过输出变量的内存地址来验证

  1. dic1 = {
  2. '小明': 98
  3. }
  4. print("使用clear方法前,dic1内存地址为", id(dic1))
  5. dic1.clear()
  6. print(dic1)
  7. print("使用clear方法后,dic1内存地址为", id(dic1))
  8. print("\n\n分割线"+"*"*30 + "\n"*2)
  9. dic2 = {
  10. '小明': 98
  11. }
  12. print("赋值空字典之前,dic1内存地址为", id(dic2))
  13. dic2 = {}
  14. print(dic2)
  15. print("赋值空字典之后,dic1内存地址为", id(dic2))

程序输出结果为

  1. 使用clear方法前,dic1内存地址为 4352796640
  2. {}
  3. 使用clear方法后,dic1内存地址为 4352796640
  4. 分割线******************************
  5. 赋值空字典之前,dic1内存地址为 4729716312
  6. {}
  7. 赋值空字典之后,dic1内存地址为 4729716168

clear是清空字典,而将一个空字典赋值给变量,并不是清空,只是修改了变量的引用而已。

2. copy, 返回字典的浅复制

  1. dic1 = {
  2. '小明': 98
  3. }
  4. dic2 = dic1.copy()
  5. print(dic1)
  6. print(dic2)

程序输出结果为

  1. {'小明': 98}
  2. {'小明': 98}

dic2是dic1的复制品,他们的内容一模一样,在python中,还有一个模块,可是实现数据的复制功能,它就是copy模块

  1. import copy
  2. dic1 = {
  3. '小明': 98
  4. }
  5. dic2 = copy.copy(dic1)
  6. print(dic1)
  7. print(dic2)

这两段代码都实现了浅复制,浅复制是一种危险的复制,建议你不要使用,因为这种复制并没有创建新的对象,因此,你对dic2的修改会影响到dic1

  1. dic1 = {
  2. 'stu': ['小明', '小红']
  3. }
  4. dic2 = dic1.copy()
  5. dic2['stu'].append('小刚')
  6. print(dic1)

程序输出结果为

  1. {'stu': ['小明', '小红', '小刚']}

关于对象的深拷贝和浅拷贝,会有专门的章节进行讲解

3. fromkeys,以指定key创建一个新的字典

  1. stu_dict = dict.fromkeys(['小明', '小刚'], 90)
  2. print(stu_dict)

程序输出结果为

  1. {'小明': 90, '小刚': 90}

fromkeys方法接受两个参数,第一个参数是序列,可以是列表,也可以是元组,方法将以这个序列里的元素做key,生成新的字典。value由第二个参数来决定,我在代码里传入参数90,所有key所对应的value就都是90,如果不传这个参数,默认value为None

4. get,返回指定key的值

get方法,是一种安全的获取value的方法,如果key不存在,则返回default,default可以由你来指定,如果你不指定,则默认为None

  1. empty_dict = {}
  2. print(empty_dict.get('python'))
  3. print(empty_dict.get('python', 100))

程序输出结果

  1. None
  2. 100

5. items(),成对返回所有key和value

items()方法通常被用在字典遍历的场景下

  1. score_dict = {
  2. '小明': 96,
  3. '小刚': 98,
  4. '小红': 94
  5. }
  6. for key, value in score_dict.items():
  7. print(key, value)

items()方法返回一个可迭代对象,使用for循环遍历这个可迭代对象时,得到的是一个元组,元组内包含key和 value
下面的代码向你揭示items()方法返回的对象的本质面目

  1. from collections import Iterable
  2. score_dict = {
  3. '小明': 96,
  4. '小刚': 98,
  5. '小红': 94
  6. }
  7. iter_obj = score_dict.items()
  8. print(isinstance(iter_obj, Iterable))
  9. for item in iter_obj:
  10. print(item)

程序输出结果为

  1. True
  2. ('小明', 96)
  3. ('小刚', 98)
  4. ('小红', 94)

6. keys,返回字典所有的key

  1. score_dict = {
  2. '小明': 96,
  3. '小刚': 98,
  4. '小红': 94
  5. }
  6. keys = score_dict.keys()
  7. print(keys, type(keys))
  8. for key in keys:
  9. print(key)

程序输出结果

  1. dict_keys(['小明', '小刚', '小红']) <class 'dict_keys'>
  2. 小明
  3. 小刚
  4. 小红

keys()方法在py2.7里,返回的是包含了所有key的列表,但在py3.6中,返回的是可迭代对象,遍历这个对象,就可以得到字典所有的key

7. values,返回字典所有value

  1. score_dict = {
  2. '小明': 96,
  3. '小刚': 98,
  4. '小红': 94
  5. }
  6. values = score_dict.values()
  7. print(values, type(values))
  8. for value in values:
  9. print(value)

values()方法返回的是一个可迭代对象,遍历这个可迭代对象,可以获得字典所有的value

8. setdefault,为key设置对应的默认值

这个方法和get有些类似,如果key不存在,则增加新的键值对,如果key已经存在,则不做任何操作

  1. score_dict = {
  2. '小明': 96,
  3. '小刚': 98,
  4. '小红': 94
  5. }
  6. score_dict.setdefault('小明', 100) # 小明这个key已经存在,因此这行语句不产生任何影响
  7. score_dict.setdefault('小丽', 97) # 小丽这个key不存在,增加新的键值对,key为小丽,value为97
  8. print(score_dict)

程序输出结果

  1. {'小明': 96, '小刚': 98, '小红': 94, '小丽': 97}

9. update 更新字典

一般的使用模式是dic1.update(dic2),将dic2的内容更新到dic1中

  1. score_dict = {
  2. '小明': 96,
  3. '小刚': 98,
  4. '小红': 94
  5. }
  6. score_dict2 = {
  7. '小明': 100,
  8. '小丽': 98,
  9. }
  10. score_dict.update(score_dict2)
  11. print(score_dict)

如果一个key,在两个字典中都存在,则value的最终结果取决于dic2

10. pop 删除键值对

不论是使用del 还是使用pop方法删除字典里的键值对,如果key不存在都会引发KeyError异常,pop与del的不同之处在于,pop会返回所删除键值对的value

  1. score_dict = {
  2. '小明': 96,
  3. '小刚': 98,
  4. '小红': 94
  5. }
  6. del score_dict['小红']
  7. print(score_dict.pop('小明'))
  8. print(score_dict)

程序输出结果

  1. 96
  2. {'小刚': 98}