Python 字典
列表适合保存一组同类数据,但只能通过位置找到元素。现在有一个新需求:保存学生的姓名、年龄和成绩,并且能直接根据字段名称找到对应内容,应该如何组织数据?
如果只看到 "小明"、18、95 这些值,很难判断每个值表示什么。字典可以同时保存字段名称和对应数据。
案例:保存学生信息
需求:保存一名学生的姓名、年龄和成绩,并通过字段名称读取这些数据。
student = {
"name": "小明",
"age": 18,
"score": 95,
}
print(student)字典使用键值对保存数据。每个键对应一个值:
key表示键,可以理解成字段名称value表示值,也就是字段对应的数据
字典创建
字典使用 {} 创建。键和值之间用冒号分隔,多个键值对之间用逗号分隔:
student = {
"name": "小明",
"age": 18,
"gender": "男",
"height": 1.65,
}创建字典时,先记住三个基本特点:
- 每个键必须唯一
- 值可以是任意数据类型
- 常见的键使用字符串
如果同一个字段被重复写入,后面的值会覆盖前面的值:
student = {
"name": "小明",
"name": "小红",
}
print(student)运行结果:
{'name': '小红'}需要从空数据开始收集信息时,可以使用 {} 或 dict() 创建空字典:
student1 = {}
student2 = dict()
print(student1)
print(student2)键值访问
字典不使用位置索引,而是通过键取得对应的值。读取数据前,先判断键是否一定存在。
方括号访问
student = {
"name": "小明",
"age": 18,
"score": 95,
}
print(student["name"])
print(student["score"])运行结果:
小明
95如果能够确定键一定存在,可以使用方括号;如果键不存在,方括号取值会抛出 KeyError:
KeyError: 'city'get() 默认值访问
如果键可能不存在,使用 get() 可以避免异常。找不到键时它默认返回 None,也可以设置一个默认值:
student = {
"name": "小明",
"age": 18,
}
print(student.get("name"))
print(student.get("city"))
print(student.get("city", "暂未填写"))运行结果:
小明
None
暂未填写可以按数据是否完整来选择:
- 确定键一定存在,使用
dictionary[key] - 键可能不存在,希望提供默认值,使用
dictionary.get(key, default)
字典增删改
学生信息录入后,年龄可能变化,也可能新增城市、删除无效字段。字典是可变容器,可以直接调整其中的键值对。
键值新增与修改
新增和修改使用相同的语法,区别在于键是否已经存在:
student = {
"name": "小明",
"age": 18,
}
# 键已经存在,修改原来的值
student["age"] = 19
# 键不存在,新增一个键值对
student["city"] = "长沙"
print(student)运行结果:
{'name': '小明', 'age': 19, 'city': '长沙'}键值删除
如果需要删除一个字段并继续使用它的值,pop() 会根据键删除数据,并返回被删除的值:
student = {
"name": "小明",
"age": 18,
"city": "长沙",
}
city = student.pop("city")
print(city)
print(student)运行结果:
长沙
{'name': '小明', 'age': 18}只需要删除而不需要返回值时,可以使用 del student["age"];需要清空整个字典时,使用 clear()。
嵌套数据访问
如果学生信息还包含地址和爱好,单层字典是否足够?字典的值可以继续保存列表、字典等容器,下面用嵌套结构保存这些信息:
student = {
"name": "正心",
"age": 18,
"gender": "男",
"height": 1.65,
"info": {
"address": "中国·湖南·长沙",
"hobby": ["吃饭", "睡觉", "写代码"],
},
}
print(student["name"])
print(student["info"]["address"])
print(student["info"]["hobby"][0])取嵌套数据时,按“外层字典 → 内层字典 → 列表元素”的路径逐层分析:
student["info"]先取得内层字典student["info"]["hobby"]再取得爱好列表student["info"]["hobby"][0]最后取得列表中的第一个爱好
修改嵌套数据时,沿着同样的路径定位目标:
student["info"]["address"] = "中国·广东·深圳"
student["info"]["hobby"].append("摄影")
print(student["info"])案例:提取音乐接口数据
问题:接口返回的数据层级较深,如何从中提取歌手、歌名和音乐地址?下面的数据和开发中常见的接口返回结果很像。
data = {
"state": True,
"errno": 22000,
"errmsg": "",
"elapsed_time": "0.0122",
"data": {
"artist": [
{
"artistCode": "A10047720",
"birthday": "1983-07-17",
"gender": "男",
"name": "薛之谦",
"artistType": 38,
"artistTypeName": "歌手",
"pic": "https://img01.dmhmusic.com/0101/M00/F8/AE/ChR45V7XEJWAZJ8KAAL8wj6aS4w115.jpg",
"region": "",
}
],
"cpId": 23,
"pic": "https://img01.dmhmusic.com/0412/M00/FE/96/ChAKEl_IzFiAUZyEAA-Q7DRXeIo850.jpg",
"title": "你还要我怎样",
"duration": 310,
"assetId": "T10038986648",
"genre": "流行",
"albumTitle": "意外",
"id": "T10038986648",
"lang": "中文",
"afReplayGain": -0.370003,
"albumAssetCode": "P10001640807",
"releaseDate": "2013-11-11T00:00:00.000Z",
"isrc": "CNA651302299",
"sort": 3,
"meanVolume": -17.1,
"maxVolume": 0,
"lyric": "https://static-qianqian.taihe.com/0412/M00/FE/96/ChAKEl_IzFiAA1agAAAG_sbemkw066.lrc",
"pay_model": 2,
"TSID": "T10038986648",
"allRate": ["320", "128", "3000", "64"],
"pushTime": "2019-03-05T16:42:51+08:00",
"downTime": "2037-01-01T00:00:00+08:00",
"bizList": ["sdk_cpm"],
"bits": 16,
"path": "https://audio04.dmhmusic.com/71_53_T10038986648_128_4_1_0_sdk-cpm/cn/0208/M00/E5/61/ChR46119DrGAW4d4AEvErRDwLyg867.mp3?xcode=c1a597cce27ff185785e95c6cf346f934713ee4",
"size": 4965549,
"rate": 128,
"hashcode": "cee20c55e4518f0511c101f76c2914c0ba9895b9",
"format": "mp3",
"filemd5": "feacadc129c14b6418d7bd12864b1757",
"expireTime": 1609416230,
"isFavorite": 0,
"isVip": 0,
},
}先把数据结构拆成访问路径:
- 最外层的
"data"对应一个字典 - 内层的
"artist"对应一个列表 - 列表中的第一个元素又是字典
- 歌手姓名保存在这个字典的
"name"中
参考代码
artist = data["data"]["artist"][0]["name"]
title = data["data"]["title"]
path = data["data"]["path"]
print("歌手:", artist)
print("歌名:", title)
print("音乐地址:", path)预期输出:
歌手:薛之谦
歌名:你还要我怎样
音乐地址:https://audio04.dmhmusic.com/71_53_T10038986648_128_4_1_0_sdk-cpm/cn/0208/M00/E5/61/ChR46119DrGAW4d4AEvErRDwLyg867.mp3?xcode=c1a597cce27ff185785e95c6cf346f934713ee4字典方法
在 IPython 中输入 dictionary. 后按 Tab,可以查看字典支持的方法。先掌握下面这些常用操作:
clear items setdefault
copy keys update
fromkeys pop values
get popitem| 方法 | 作用 |
|---|---|
get(key, default=None) | 取得指定键的值;键不存在时返回默认值 |
keys() | 取得所有键 |
values() | 取得所有值 |
items() | 取得所有键值对,遍历时常用 |
update(other_dict) | 使用另一个字典新增或更新键值对 |
pop(key) | 删除指定键并返回对应的值 |
clear() | 清空字典 |
copy() | 复制字典 |
student = {
"name": "小明",
"age": 18,
}
student.update({"age": 19, "city": "长沙"})
print(student.keys())
print(student.values())
print(student.items())
print(student)update() 中的键已经存在时会更新,键不存在时会新增。
字典遍历
键遍历
如果只需要字段名称,直接遍历字典,默认取得每一个键:
student = {
"name": "小明",
"age": 18,
"city": "长沙",
}
for key in student:
print(key, student[key])也可以写成 for key in student.keys(),运行效果相同。
键值对遍历
如果需要同时处理字段名称和字段内容,items() 可以在每轮循环中取得键和值:
student = {
"name": "小明",
"age": 18,
"city": "长沙",
}
for key, value in student.items():
print(key, value)只需要值时,可以遍历 student.values()。需要成对处理字段和值时,items() 最直接。
案例:统计字符
问题:如何统计一句话中每个字符出现的次数?
text = "Beautiful is better than ugly."
char_count = {}
for char in text:
if char not in char_count:
char_count[char] = 1
else:
char_count[char] += 1
for char, count in char_count.items():
print(f"字符 {char!r} 出现了 {count} 次")功能分析:
- 创建空字典
char_count - 依次取出字符串中的字符
- 字符第一次出现时,将次数设置为
1 - 字符再次出现时,将原次数增加
1 - 遍历字典并输出统计结果
这里直接使用 char in char_count 判断键是否存在,不需要额外调用 keys()。
字符频次排序
如果还需要按次数从多到少输出,就把字典的键值对转换成列表,再按每个元素的第二项排序。
下面的 get_count() 是排序时使用的辅助函数。函数会在后面的章节详细学习,这里先观察运行效果:
item_list = list(char_count.items())
def get_count(item):
return item[1]
item_list.sort(key=get_count, reverse=True)
for char, count in item_list:
print(f"字符 {char!r} 出现了 {count} 次")小结
本节先回答五个问题:
- 为什么使用字典?因为它能用键值对表达字段与数据的对应关系
- 如何安全取值?确定键存在时用方括号,键可能缺失时用
get() - 如何维护字段?
dictionary[key] = value既能新增,也能修改,删除可使用pop()、del或clear() - 如何读取嵌套数据?沿着键和索引从外到内逐层访问
- 如何批量处理数据?根据需求遍历键、值或键值对,常用
items()
练习
- 创建一个字典,保存自己的姓名、年龄和城市
- 取出姓名,使用
get()读取一个不存在的键 - 新增
hobby,修改年龄,再删除城市 - 使用
for循环打印所有键值对 - 统计字符串
"hello python"中每个字符出现的次数