# Python 字典：按名字取数据，为什么比按位置更靠谱

承接上一篇的列表：用大括号把“键→值”成对装起来，按键取值、增改与删除，用 in 判断键在不在、用 get 带默认值兜底，并让 for 配合 items() 一次取出所有键和值。

> 字典与键值对 · 循环与重复执行 · 自动化入门 · 约 7 分钟 · 10 月 08 日

## 本篇要点

1. 列表按位置取值（`名单[0]`），位置会随表格加列、删列而改变，所以用位置记数据不可靠；字典按名字取值，名字和数据成对写在一起，才贴合一行的表格记录。
2. 字典用大括号写：`员工 = {"姓名": "张三", "部门": "财务", "工资": 8000}`，每一项用冒号分成键和值，项与项之间用逗号。
3. 取值时方括号里放的是键，通常是带引号的字符串，例如 `员工["姓名"]`；键不存在会报 KeyError，和列表越界报 IndexError 是同一类错误。
4. 同一个字典里键是唯一的，重复写同一个键会让后一个覆盖前一个；正因为键唯一，`员工["工资"]` 无论字典顺序怎么变，拿到的都是工资那一项。
5. `员工["电话"] = 值` 一句同时承担“新增”和“修改”：键本来没有就加一对，键已存在就换掉旧值；删除一对用 `del 员工["电话"]`。
6. `"工资" in 员工` 检查的是键在不在，不检查值；`员工.get("电话", "没登记")` 在键不存在时返回默认值而不报错。
7. 不要用 `if 员工.get("工资"):` 判断键是否存在，因为 0 和空字符串会被当成“不算数”而误判成键不存在；判断存在用 `in`。
8. 把字典交给 for，默认每轮交出一个键；要同时拿到键和值，写 `for 键, 值 in 员工.items():`，因为 `items()` 每轮交出的是一对（键, 值），左边的两个变量名按顺序分别接住。
9. 键通常用字符串或数字；值可以是数字或字符串，往后还会遇到值是列表或另一个字典的情况。
10. Python 3.7 起字典保留插入顺序，所以遍历字典得到的就是你放入键时的顺序。

---

上一篇的列表解决了“一个变量装下一串数据”，但它取数据靠数位置：`名单[0]` 是第一个，`名单[2]` 是第三个。装一串同类的东西，这没问题；可如果要装的是一行表格记录，位置就会变成麻烦。

## 列表在这里会咬人一口

假设你要处理工资表里的一个人，顺手用列表装：

```python
员工 = ["张三", "财务", 8000]
print(员工[2])   # 8000
```

今天跑得好好的。下个月表格加了一列“入职日期”，插在部门和工资之间：

```python
员工 = ["张三", "财务", "2021-03-02", 8000]
print(员工[2])   # 2021-03-02
```

代码一个字没改，结果已经错了。问题不在你写得不小心，而在 `员工[2]` 这句话的意思是“第 3 个东西”——它记住的是位置，不是“这个东西是什么”。表格加列、删列、调整顺序，位置就全变。

列表本来就是给“排成一队、挨个数”的数据用的。可一行表格记录里，各项之间的关系不是“排在第几位”，而是“它叫什么名字”：这一项叫姓名，那一项叫部门，另一项叫工资。

## 字典：把名字和数据配成对写进去

让数据自己带名字，就要换一种容器，叫**字典**（dict）。写法是用大括号把一串“名字和它的数据”配成对装起来：

```python
员工 = {"姓名": "张三", "部门": "财务", "工资": 8000}
```

分解着看这一行：

- 外面的大括号 `{}` 是字典的标志。
- 里面每一项都是一“对”，中间用**冒号**分成左右两半：左边那个名字叫**键**（key），右边挂着的东西叫**值**（value）。
- 一对和一对之间用**逗号**隔开。

读它的方式：键就是你贴在盒子上的名字牌，值就是这个牌子下面挂的东西。字典存在的意义就是——你报一个名字牌，它把对应的东西交给你，中间隔着多少别的项都不影响。[1]

对照上一篇的列表看：列表是“一排格子，按顺序数”；字典是“一排带名牌的格子，按名牌找”。

## 按键取值

取值的写法和列表长得几乎一样，都是方括号，但方括号里放的东西换了：

```python
print(员工["姓名"])   # 张三
print(员工["工资"])   # 8000
```

列表里 `名单[0]` 的方括号放的是位置编号，是数字；字典里 `员工["姓名"]` 的方括号放的是键，一般是字符串，所以**要带引号**。这是两种容器最容易混的地方：同样一个方括号，配字典时里面写的是“名字”，不是“第几个”。

名字报错了会怎样？字典里没有的键，用方括号硬取会报 `KeyError`：

```python
print(员工["电话"])
# KeyError: '电话'
```

这和列表取越界时报 `IndexError` 是同一类事——容器里没这个东西，Python 不会替你猜，直接停下来告诉你。[1]

## 键不能重名，这条规矩带来了可靠

字典有一条硬性要求：**同一个字典里，键是唯一的**。你要是写了两个 `"工资"`，后一个会把前一个覆盖掉。

正是这条规矩，让“按名字取”变得可靠。不管这张表前面加了多少列、顺序怎么调，只要“工资”这个键还在，`员工["工资"]` 拿到的就一定是工资那一项。你关心的是“哪一项”，而不是“第几项”——数据一旦自带名字，读代码时你也不用一边看一边数位置了。

## 加一项和改一项，是同一个写法

字典建好之后还能长，也能改：

```python
员工["电话"] = "13800001111"   # 这个键本来没有 → 加进一对新的
员工["工资"] = 8500            # 这个键已经有了 → 把值换成 8500
print(员工)
# {'姓名': '张三', '部门': '财务', '工资': 8500, '电话': '13800001111'}
```

请注意这两行的形状完全相同，右边都是 `=` 赋值。Python 在赋值时先看键在不在：键不在字典里，就新增一对；键已经在里面了，就把旧值替换掉。[1] 你不用记两套语法，因为“有就改、没有就加”本来就是同一个动作。

删除一对用 `del`：

```python
del 员工["电话"]
```

删完之后再写 `员工["电话"]`，就会报 `KeyError`——这一对是真的没了。

## 先问一句“在不在”：in

既然取不存在的键会报错，那有时候你会想先问一声：

```python
if "工资" in 员工:
    print(员工["工资"])
```

把字典放在 `in` 右边，得到的是 `True` 或 `False`：这个键在不在字典里。[2] 这是上一篇学过的 `in` 的一种新用法，判断的对象从“某项在不在列表里”换成了“某个键在不在字典里”。

这里有一个必须记住的点：**`in` 只检查键，不检查值**。

```python
"财务" in 员工     # False，虽然“财务”确实作为值存在于字典里
"姓名" in 员工     # True
```

想找某个值在不在，得专门写成 `"财务" in 员工.values()`，那是另一回事。日常用的时候，先默认 `in` 看的是键。

除了先问再取，还有一种更顺手的读法 `get()`：

```python
print(员工.get("电话"))             # None
print(员工.get("电话", "没登记"))   # 没登记
```

`get()` 的写法带着点号，和上一篇 `append()` 一样，是字典自带的方法。它按键取值，但如果键不存在，它不报错：只给一个参数时返回 `None`，给第二个参数时就返回你指定的那个默认值。[1][3] “电话这一列不是每个人都有”这种场景，用它比先 `in` 再取要短。

不过 `get()` 有个坑要提前知道。看这句：

```python
if 员工.get("工资"):
    ...
```

这么写是错的。`if` 判断的是“这个值算不算数”，而 `0`、`0.0`、空字符串 `""` 在 Python 里都算“不算数”。所以工资真的是 0 的人，会被这句话当成“没有工资这一项”而跳过。要判断“有没有这个键”，就用 `in`；要判断“有键而且值不是空”，写 `员工.get("工资") is not None`。[3] 处理表格时数值列出现 0 太常见了，这个区别迟早会用到。

## 用 for 一次取出所有键和值

字典也能交给 `for`。直接把字典放在 `in` 右边，每轮交出来的是**键**：

```python
员工 = {"姓名": "张三", "部门": "财务", "工资": 8000}

for 键 in 员工:
    print(键)
```

要顺带拿值，就在循环体里用这个键去取：`print(键, 员工[键])`。

更常用的是一个一步到位的写法：

```python
for 键, 值 in 员工.items():
    print(f"{键}：{值}")
```

输出是：

```
姓名：张三
部门：财务
工资：8000
```

这里的循环写法照旧——冒号、缩进、每轮回头，都和之前一样。只有左边多了一个新东西：写了 `键, 值` 两个变量名，中间用逗号隔开。

原因在 `items()` 交出来的东西。`items()` 是字典自带的方法，它每一轮交出的不是单个值，而是**一对**（键, 值），例如 `("姓名", "张三")`。[1][3] 左边写了两个名字，Python 就按顺序把这一对拆开：第一个装进 `键`，第二个装进 `值`，然后执行循环体。你可以把它理解成“一对东西，正好放进两个盒子”，这两个盒子只在这一轮里用，下一轮换上新的一对。

如果只关心值，不关心名字，可以写 `员工.values()`；如果只关心键，`for 键 in 员工` 就够了。

还有一件关于顺序的事：从 Python 3.7 起，字典会记住你放进去的顺序，遍历出来就是插入顺序，不是打乱的。[1] 所以上面那段代码的输出顺序，和你写字典时的顺序一致。

## 键和值分别能放什么

键通常用字符串或数字——它就是一个名字牌，越简单越好。值没有这种限制，数字和字符串都能放；以后你还会遇到值本身是列表、甚至又是一个字典的写法，那时候再讲。这一篇里的值都是简单的数字和字符串。

## 一句话收束

列表按位置找，字典按名字找。表格里的一行记录，每一项之间的关系是“它叫什么”，所以用字典装最贴合：加列、删列、换顺序，只要键还在，取值的代码就不用动。

下一步的问题是：一行记录装进了一个字典，那几百行呢？答案是让很多个字典排成一队，装进一个列表——也就是“列表里装字典”。那正是读取表格、逐行处理的核心结构，我们下一篇来讲。

## 术语表

- 字典（dict）：用大括号把“名字和数据”成对装起来的容器，和列表一样是可以赋给变量的值，区别在于它按名字找，不按位置数。
- 键（key）：写在冒号左边、贴在数据上的名字牌，同一个字典里不能重复，通常用字符串或数字。
- 值（value）：写在冒号右边、挂在某个键下面的东西，这篇里是数字和字符串。
- 按键取值：`字典[键]`，方括号里放的是键本身；键不存在时报 KeyError。
- in 检查键：`"工资" in 员工` 得到 True/False，只判断键在不在，不看值。
- get()：字典自带的方法，按键取值但键不存在时不报错，可以指定一个默认值顶替。
- items()：字典自带的方法，每轮交出一对（键, 值），配合 `for 键, 值 in ...` 一次拿到两样。
- 插入顺序：字典按你放入键的先后保存顺序，Python 3.7 起遍历出来就是这个顺序。

## 来源

1. [Python 官方教程“字典”一节：键值对写法、按键取值、KeyError、del、get()、in 与插入顺序](https://docs.python.org/3/tutorial/datastructures.html#dictionaries)
2. [Python 官方文档“映射类型 dict”：get()、keys()、values()、items() 等方法的定义与返回值](https://docs.python.org/3/library/stdtypes.html#mapping-types-dict)
3. [字典方法对照表与 in / get() 的用法说明，包括“键存在但值为假”时的判断差别](https://runestone.academy/ns/books/published/fopp/Dictionaries/Dictionarymethods.html)

---

原文：https://pangzhengboyin.com/articles/python-dict-key-value-lookup-2a579e4b

> **庞征博引** · 想学的，慢慢都会
>
> 庞征博引是把想学的东西写成连载的 AI 学习工具。说出想学什么，它会先了解你的基础，再把主题写成一篇篇 5–10 分钟能读完的文章；边读边问，接下来学什么跟着你走。这篇就是这样写出来的。
>
> 开始你自己的连载 → https://pangzhengboyin.com
