defaultdict
: 无条件字典的进化
虽然Python有许多外部库,但仅仅理解标准库就足以编写强大的实用代码。本文将深入探讨其中的 collections.defaultdict
。
通过这篇文章,您将超越简单的概念介绍,明确理解 defaultdict
应该在何时、为何以及如何使用。
如果您对作为collections系列第一的Counter类感兴趣,建议阅读上一篇文章。 Python标准库 ① - collections.Counter
1. 基本概念: defaultdict
是什么?
defaultdict
是Python标准库 collections
模块中包含的特殊字典(dict)的子类。普通字典访问不存在的键时会抛出 KeyError
,但 defaultdict
可以指定自动生成默认值的函数(工厂函数),这样代码会更加简洁并避免错误。
2. 基本用法
from collections import defaultdict
d = defaultdict(int)
d['apple'] += 1
print(d) # defaultdict(<class 'int'>, {'apple': 1})
这里 int()
返回默认值 0
。访问不存在的键 'apple'
时,自动生成 0
,然后执行 +1
。
3. 多种默认值示例
from collections import defaultdict
# 默认值: 0 (int)
counter = defaultdict(int)
counter['a'] += 1
print(counter) # defaultdict(<class 'int'>, {'a': 1})
# 默认值: 空列表
group = defaultdict(list)
group['fruit'].append('apple')
group['fruit'].append('banana')
print(group) # defaultdict(<class 'list'>, {'fruit': ['apple', 'banana']})
# 默认值: 空集合
unique_tags = defaultdict(set)
unique_tags['tags'].add('python')
unique_tags['tags'].add('coding')
print(unique_tags) # defaultdict(<class 'set'>, {'tags': {'python', 'coding'}})
# 默认值: 自定义初始值
fixed = defaultdict(lambda: 100)
print(fixed['unknown']) # 100
4. 实战示例
1. 统计单词频率
words = ['apple', 'banana', 'apple', 'orange', 'banana']
counter = defaultdict(int)
for word in words:
counter[word] += 1
print(counter)
# defaultdict(<class 'int'>, {'apple': 2, 'banana': 2, 'orange': 1})
👉 Counter vs defaultdict
单词频率统计更适合使用 collections.Counter()
,在需要进行统计或排名分析的情况下,使用 Counter
是更为合适的。但对于简单累计计数, defaultdict(int)
使用起来也相当简便。
2. 按组整理日志
logs = [
('2024-01-01', 'INFO'),
('2024-01-01', 'ERROR'),
('2024-01-02', 'DEBUG'),
]
grouped = defaultdict(list)
for date, level in logs:
grouped[date].append(level)
print(grouped)
# defaultdict(<class 'list'>, {'2024-01-01': ['INFO', 'ERROR'], '2024-01-02': ['DEBUG']})
3. 整理去重标签
entries = [
('post1', 'python'),
('post1', 'coding'),
('post1', 'python'),
]
tags = defaultdict(set)
for post, tag in entries:
tags[post].add(tag)
print(tags)
# defaultdict(<class 'set'>, {'post1': {'python', 'coding'}})
5. 注意事项
defaultdict
内部存储了默认值生成器,因此在repr()
时可能与普通dict
的表现不同。- 在JSON序列化时可能会出现问题。使用
dict(d)
转换后处理是安全的。 - 默认值只在访问
[]
时生成,而如果通过get()
访问,则不会生成默认值。
from collections import defaultdict
d = defaultdict(list)
print(d.get('missing')) # None
print(d['missing']) # []
6. 何时使用? – defaultdict
的三大优势
defaultdict
在大量使用 dict
+ 条件语句
模式的情况下,同时提高了可读性、可维护性和安全性。尤其在以下三种情况下,您会感觉“哦,这个肯定更适合用defaultdict!”
6-1. 无条件计数/累计的聚合代码
from collections import defaultdict
# 普通 dict
counts = {}
for item in items:
if item not in counts:
counts[item] = 0
counts[item] += 1
# defaultdict
counts = defaultdict(int)
for item in items:
counts[item] += 1
✔ 条件语句消失,代码变得简洁,错误的可能性减少了。
✔ 特别适合日志分析、单词计数等大数据处理。
6-2. 当累积列表/集合时替代 setdefault
from collections import defaultdict
# 普通 dict
posts = {}
for tag, post in data:
if tag not in posts:
posts[tag] = []
posts[tag].append(post)
# defaultdict
posts = defaultdict(list)
for tag, post in data:
posts[tag].append(post)
✔ 比 setdefault()
更直观,在循环中也更简洁。
✔ 适用于数据分组的结构。
6-3. 自动化嵌套字典的初始化
# 普通字典
matrix = {}
if 'x' not in matrix:
matrix['x'] = {}
matrix['x']['y'] = 10
# defaultdict 嵌套
matrix = defaultdict(lambda: defaultdict(int)) # 每当没有键时都会自动生成 defaultdict(int) 的实例
matrix['x']['y'] += 10
✔ 可以轻松创建嵌套数据结构,对多维字典操作非常有利。
✔ 在数据挖掘、解析、树结构存储等方面表现出强大的优势。
lambda: defaultdict(int)
在内部结构上为每当没有键时返回defaultdict(int)
,自动生成嵌套的字典。
7. 总结
collections.defaultdict
对于初学者来说,可能只是简单的 dict
扩展,但随着使用的深入,您会体会到它是让代码更明晰和安全的结构性工具。
- 可以不担心
KeyError
使用字典。 - 无需条件语句即可对数据进行分组和累积。
- 可以直观地构建嵌套字典。
# 通过 defaultdict 安全且简洁地处理的示例
salaries = defaultdict(int)
for dept, amount in records:
salaries[dept] += amount
如果您能用一行代码来防止错误 + 提高可读性 + 增强可维护性,
那么 defaultdict
就不仅仅是简单的便利功能,而是Python的思维方式的核心工具。
接下来讨论的主题是 pathlib
。
这种现代的方法将文件/目录处理的代码变得面向对象,从初学者到中级开发者都能受益。熟悉 os.path
的开发者会感到“哇!比 os.path 简单多了!”期待您的下篇文章。
댓글이 없습니다.