Python中defatultdict的概念、作用、常见用法举例 详解

在 Python 中,collections.defaultdict 是一个非常有用的数据结构,它是 dict 的子类,
区别在于它可以为不存在的键提供一个默认值,而不需要显式检查键是否存在。
以下是 defaultdict 的常见用法举例,涵盖不同场景:
 

1. 基本用法:为不存在的键提供默认值

defaultdict 需要一个工厂函数(如 int, list, set 等)来生成默认值。

from collections import defaultdict

# 使用 int 作为默认工厂,键不存在时返回 0
d = defaultdict(int)
d['a'] += 1  # 不需要初始化,键 'a' 自动赋值为 0 + 1
print(d)  # 输出: defaultdict(<class 'int'>, {'a': 1})

# 使用 list 作为默认工厂,键不存在时返回空列表
d = defaultdict(list)
d['a'].append(1)  # 不需要初始化,直接追加
d['b'].append(2)
print(d)  # 输出: defaultdict(<class 'list'>, {'a': [1], 'b': [2]})

# 使用 set 作为默认工厂,键不存在时返回空集合
d = defaultdict(set)
d['a'].add(1)  # 直接添加
d['a'].add(2)
print(d)  # 输出: defaultdict(<class 'set'>, {'a': {1, 2}})

 

2. 计数器(统计出现次数)

常用于统计元素出现次数,替代手动检查和初始化。

from collections import defaultdict

# 统计字符串中每个字符的出现次数
text = "hello world"
char_count = defaultdict(int)
for char in text:
    char_count[char] += 1
print(char_count)  # 输出: defaultdict(<class 'int'>, {'h': 1, 'e': 1, 'l': 3, 'o': 2, ' ': 1, 'w': 1, 'r': 1, 'd': 1})

 

3. 分组(按键分组数据)

用于将数据按某个键分组,类似 SQL 的 GROUP BY。

from collections import defaultdict

# 按年龄分组人员
people = [
    {"name": "Alice", "age": 25},
    {"name": "Bob", "age": 30},
    {"name": "Charlie", "age": 25}
]
age_groups = defaultdict(list)
for person in people:
    age_groups[person["age"]].append(person["name"])
print(age_groups)  # 输出: defaultdict(<class 'list'>, {25: ['Alice', 'Charlie'], 30: ['Bob']})

 

4. 嵌套 defaultdict

用于处理多级嵌套字典,避免多次检查键是否存在。

from collections import defaultdict

# 创建嵌套 defaultdict
nested_dict = defaultdict(lambda: defaultdict(int))
nested_dict['a']['b'] += 1  # 直接操作嵌套键
nested_dict['a']['c'] += 2
print(nested_dict)  # 输出: defaultdict(<function <lambda> at ...>, {'a': defaultdict(<class 'int'>, {'b': 1, 'c': 2})})

 

5. 累加值(累加数字或列表)

用于累加值或构建复杂数据结构。

from collections import defaultdict

# 累加每个键的值
scores = [('Alice', 100), ('Bob', 200), ('Alice', 150)]
score_sum = defaultdict(int)
for name, score in scores:
    score_sum[name] += score
print(score_sum)  # 输出: defaultdict(<class 'int'>, {'Alice': 250, 'Bob': 200})

# 收集每个键的多个值
data = [('cat', 'meow'), ('dog', 'woof'), ('cat', 'purr')]
sounds = defaultdict(list)
for animal, sound in data:
    sounds[animal].append(sound)
print(sounds)  # 输出: defaultdict(<class 'list'>, {'cat': ['meow', 'purr'], 'dog': ['woof']})

 

6. 自定义默认值

可以自定义默认值生成函数,灵活处理复杂场景。

from collections import defaultdict

# 自定义默认值为特定的字符串
d = defaultdict(lambda: "Unknown")
d['a'] = "Apple"
print(d['a'])  # 输出: Apple
print(d['b'])  # 输出: Unknown

 

7. 与普通字典的对比

使用普通字典时,需要手动检查键是否存在,否则会抛出 KeyError:

# 普通字典
d = {}
# 需要检查键是否存在
if 'a' not in d:
    d['a'] = 0
d['a'] += 1

# 使用 defaultdict 更简洁
d = defaultdict(int)
d['a'] += 1  # 直接操作

 

8. 复杂默认值生成

普通函数可以包含复杂的逻辑,例如根据全局状态或外部条件生成默认值。

from collections import defaultdict
from datetime import datetime

# 记录访问时间和初始计数
def default_log_entry():
    return {"count": 0, "last_access": datetime.now()}

log = defaultdict(default_log_entry)
log['user1']['count'] += 1
log['user2']['count'] += 2
print(log)
# 输出: defaultdict(<function default_log_entry at ...>, {
#     'user1': {'count': 1, 'last_access': 2023-...},
#     'user2': {'count': 2, 'last_access': 2023-...}
# })

 

注意事项

  • 性能defaultdict 的性能与普通字典几乎相同,但在处理大量键不存在的场景时更简洁高效。
  • 默认值不可变:如果使用 list 或 set 作为默认工厂,每次访问不存在的键都会返回一个新对象,不会影响其他键。
  • 序列化defaultdict 不能直接用 json.dumps 序列化,需先转换为普通字典(如 dict(d))。
posted @ 2025-04-29 14:45  AlphaGeek  阅读(112)  评论(0)    收藏  举报