|
## 为什么要用 defaultdict?
这段代码在做**字符和二元组统计**:
```python
from collections import defaultdict
uni, bi = defaultdict(int), defaultdict(int)
for s in ["我爱中国", "我爱学习", "我喜欢中国"]:
w = list(s)
for c in w: uni[c] += 1
for i in range(len(w)-1): bi[(w[i], w[i+1])] += 1
p = bi[("我","爱")] / uni["我"] # P(爱|我) = count(我,爱)/count(我)
```
### 核心原因
`defaultdict(int)` 在访问**不存在的键**时,会自动调用 `int()` 返回 `0`,因此:
| 操作 | defaultdict | 普通dict |
|------|-------------|----------|
| 访问新键 | 返回 0 | 抛出 KeyError |
| `d[key] += 1` | ✅ 直接用 | ❌ 需先初始化 |
### 关键点
代码中的 `uni[c] += 1` 和 `bi[(w[i], w[i+1])] += 1` 依赖于:
1. 首次访问某字符/二元组时,值自动初始化为 0
2. 然后 0 + 1 = 1 完成首次计数
3. 后续访问正常累加
### 执行流程示例
以 `uni["我"] += 1` 为例:
```text
第1次访问 uni["我"] → defaultdict返回0 → 0+1=1 存入
第2次访问 uni["我"] → 已存在,值为1 → 1+1=2 存入
第3次访问 uni["我"] → 已存在,值为2 → 2+1=3 存入
```
如果用普通 dict,第1次访问会直接报错 `KeyError: '我'`。
|
|
## 普通dict的等价写法
如果坚持用普通 dict,需要手动处理键不存在的情况:
### 方式一:显式检查(啰嗦)
```python
uni, bi = {}, {}
for s in ["我爱中国", "我爱学习", "我喜欢中国"]:
w = list(s)
for c in w:
if c not in uni:
uni[c] = 0
uni[c] += 1
for i in range(len(w)-1):
key = (w[i], w[i+1])
if key not in bi:
bi[key] = 0
bi[key] += 1
```
### 方式二:使用 setdefault(稍好)
```python
uni, bi = {}, {}
for s in ["我爱中国", "我爱学习", "我喜欢中国"]:
w = list(s)
for c in w:
uni[c] = uni.setdefault(c, 0) + 1
for i in range(len(w)-1):
key = (w[i], w[i+1])
bi[key] = bi.setdefault(key, 0) + 1
```
### 方式三:使用 get(最常见)
```python
uni, bi = {}, {}
for s in ["我爱中国", "我爱学习", "我喜欢中国"]:
w = list(s)
for c in w:
uni[c] = uni.get(c, 0) + 1
for i in range(len(w)-1):
key = (w[i], w[i+1])
bi[key] = bi.get(key, 0) + 1
```
### 代码行数对比
| 写法 | 新增行数 | 可读性 |
|------|----------|--------|
| defaultdict | 0 | ⭐⭐⭐ |
| get() | +1变量 | ⭐⭐ |
| setdefault() | +1变量 | ⭐⭐ |
| 显式检查 | +4行if | ⭐ |
**结论**:对于计数、累加这类"访问-修改"操作,defaultdict 是最简洁的选择。
|