杰瑞科技汇

python字典defaudict

defaultdict 是 Python 标准库 collections 模块中的一个类,它是 dict(字典)的子类,它的主要作用是为字典的键提供一个默认值,从而避免在访问或修改不存在的键时引发 KeyError 异常。

为什么需要 defaultdict

想象一个常见的场景:我们想统计一个列表中每个单词出现的次数。

使用普通字典的代码可能会是这样:

words = ['apple', 'banana', 'apple', 'orange', 'banana', 'apple']
# 使用普通字典
word_counts = {}
for word in words:
    if word in word_counts:
        word_counts[word] += 1
    else:
        word_counts[word] = 1
print(word_counts)
# 输出: {'apple': 3, 'banana': 2, 'orange': 1}

或者使用 dict.get() 方法,虽然更简洁,但依然需要处理默认逻辑:

words = ['apple', 'banana', 'apple', 'orange', 'banana', 'apple']
word_counts = {}
for word in words:
    # word 不在字典中,get() 方法会返回 0,然后我们再加 1
    word_counts[word] = word_counts.get(word, 0) + 1
print(word_counts)
# 输出: {'apple': 3, 'banana': 2, 'orange': 1}

这两种方式都能实现功能,但代码略显冗长。defaultdict 就是为了解决这个问题而生的。


defaultdict 的工作原理

defaultdict 在创建时需要一个“工厂函数”作为其第一个参数,这个工厂函数会在你尝试访问一个不存在的键时被调用,其返回值将作为该键的默认值。

语法: collections.defaultdict(factory_function)

常用的工厂函数有:

  • int: 返回整数 0,非常适合计数。
  • list: 返回空列表 [],非常适合分组。
  • set: 返回空集合 set(),非常适合去重分组。
  • dict: 返回空字典 。
  • lambda x: 0: 你也可以使用 lambda 函数创建自定义默认值。

使用 defaultdict 的示例

让我们用 defaultdict 来重写上面的单词计数例子。

示例1:单词计数 (使用 int 作为工厂函数)

from collections import defaultdict
words = ['apple', 'banana', 'apple', 'orange', 'banana', 'apple']
# 创建一个 defaultdict,并指定 int 作为默认工厂函数
# 当访问一个不存在的键时,它会自动返回 int() 的结果,也就是 0
word_counts = defaultdict(int)
for word in words:
    # 不需要检查键是否存在,直接进行 += 操作
    # 'apple' 第一次出现,word_counts['apple'] 会被自动初始化为 0,然后执行 0 + 1
    word_counts[word] += 1
print(word_counts)
# 输出: defaultdict(<class 'int'>, {'apple': 3, 'banana': 2, 'orange': 1})

对比一下:

  • 普通字典: 需要手动检查键是否存在 (if key in dict) 或使用 dict.get(key, default)
  • defaultdict: 直接操作,代码更简洁、更易读。KeyError 被自动处理了。

示例2:分组 (使用 list 作为工厂函数)

假设我们有一系列单词,想根据单词的首字母进行分组。

from collections import defaultdict
words = ['apple', 'ant', 'banana', 'ball', 'cat', 'car']
# 创建一个 defaultdict,并指定 list 作为默认工厂函数
# 当访问一个不存在的键时,它会自动返回 list() 的结果,也就是 []
grouped_words = defaultdict(list)
for word in words:
    # 将单词添加到其首字母对应的列表中
    # 'a' 第一次出现,grouped_words['a'] 会被自动初始化为 [],然后执行 append 操作
    grouped_words[word[0]].append(word)
print(grouped_words)
# 输出:
# defaultdict(<class 'list'>,
#             {'a': ['apple', 'ant'],
#              'b': ['banana', 'ball'],
#              'c': ['cat', 'car']})

这个例子完美展示了 defaultdict 在分组场景下的强大之处。

示例3:自定义默认值 (使用 lambda 函数)

假设我们有一个学生成绩列表,我们想计算每个学生的平均分,当第一次遇到一个学生时,我们希望他的总分和次数都初始化为 0

from collections import defaultdict
grades = [('Alice', 85), ('Bob', 90), ('Alice', 92), ('Bob', 88), ('Charlie', 95)]
# 我们需要一个包含两个元素的元组作为默认值:(0, 0)
# 使用 lambda 函数来创建这个元组
student_stats = defaultdict(lambda: [0, 0]) # [总分, 次数]
for name, score in grades:
    stats = student_stats[name]
    stats[0] += score  # 累加总分
    stats[1] += 1      # 增加次数
# 计算平均分
average_scores = {name: total / count for name, (total, count) in student_stats.items()}
print(student_stats)
# 输出: defaultdict(<class 'function'>, {'Alice': [177, 2], 'Bob': [178, 2], 'Charlie': [95, 1]})
print(average_scores)
# 输出: {'Alice': 88.5, 'Bob': 89.0, 'Charlie': 95.0}

defaultdict vs. dict.get()

特性 defaultdict dict.get(key, default)
初始化 在创建时指定默认值的类型。 在每次访问时指定默认值。
行为 自动填充,当你赋值给一个不存在的键时,它会自动用工厂函数创建值。 惰性返回,当你读取一个不存在的键时,它返回你指定的默认值,但不会在字典中创建这个键。
适用场景 当你希望字典中“总是”存在某个键,并且有默认值时,非常适合计数、分组等场景。 当你只想在“读取”时获取一个默认值,但不想在字典中创建新键时。
代码风格 代码更简洁,意图更明确,减少了 if/else 判断。 灵活性高,但可能在循环中重复书写默认值。

一个重要的区别: dict.get() 不会修改字典,而 defaultdict 会。

# dict.get() 不会创建新键
d = {}
print(d.get('new_key', 'default_value'))  # 输出: default_value
print(d)  # 输出: {},字典没有改变
# defaultdict 会创建新键
from collections import defaultdict
dd = defaultdict(list)
print(dd['new_key'])  # 输出: [],因为 list() 返回了空列表
print(dd)  # 输出: defaultdict(<class 'list'>, {'new_key': []}),键 'new_key' 已经被创建

defaultdict 是一个非常实用且高效的 Python 工具,当你发现自己在代码中频繁地使用 if key in my_dict 或者 my_dict.get(key, default_value) 来处理字典中可能不存在的键时,就应该考虑使用 defaultdict

核心优势:

  1. 代码简洁:消除了显式的键存在性检查,使代码更清晰、更易读。
  2. 性能更优:在某些场景下,比手动检查或使用 get() 稍快,因为它避免了函数调用的开销(虽然差异通常很小)。
  3. 意图明确:它清晰地表明了这个字典的预期行为——为缺失的键提供默认值。

掌握 defaultdict 是提升 Python 编程能力的重要一步,尤其是在处理数据聚合、统计和分组任务时。

分享:
扫描分享到社交APP
上一篇
下一篇