defaultdict 在python中的作用
collections.defaultdict
是 Python collections
模块中提供的一个字典(dict
)的子类。它与普通的字典非常相似,但有一个关键的区别:当你尝试访问一个不存在的键时,defaultdict
不会引发 KeyError
异常,而是会自动为该键生成一个默认值。
defaultdict
的基本用法
defaultdict
的构造函数需要一个“默认工厂函数”(default_factory
)作为参数。这个工厂函数是一个不接受任何参数的可调用对象,它会在访问不存在的键时被调用,并将其返回值作为该键的默认值。
Python
from collections import defaultdict# 使用 int 作为默认工厂函数,意味着当访问不存在的键时,会创建一个默认值为 0 的新条目。
# int() 会返回 0
word_counts = defaultdict(int)# 访问一个不存在的键 'apple',它会自动创建并赋值为 int() 的结果 (0)
print(word_counts['apple']) # 输出: 0# 现在 'apple' 键存在了,我们可以像普通字典一样操作它
word_counts['apple'] += 1
print(word_counts['apple']) # 输出: 1word_counts['banana'] += 1
word_counts['apple'] += 1
print(word_counts) # 输出: defaultdict(<class 'int'>, {'apple': 2, 'banana': 1})
为什么使用 defaultdict
?
defaultdict
主要用于简化代码,避免在使用普通字典时频繁地检查键是否存在。这在以下场景中特别有用:
-
计数 (Counting): 统计列表中元素的出现次数,或字符串中字符的出现次数。
使用普通字典:
Pythoncounts = {} my_list = ['apple', 'banana', 'apple', 'orange', 'banana', 'apple'] for item in my_list:if item in counts:counts[item] += 1else:counts[item] = 1 print(counts)
使用
Pythondefaultdict(int)
:from collections import defaultdict counts = defaultdict(int) my_list = ['apple', 'banana', 'apple', 'orange', 'banana', 'apple'] for item in my_list:counts[item] += 1 # 如果 'item' 不存在,会自动创建为 0,然后加 1 print(counts)
显然,
defaultdict
的代码更简洁。 -
分组 (Grouping): 将具有相同特征的元素分组。例如,根据首字母将单词分组。
使用普通字典:
Pythonwords_by_initial = {} words = ['apple', 'banana', 'apricot', 'cat', 'dog'] for word in words:initial = word[0]if initial in words_by_initial:words_by_initial[initial].append(word)else:words_by_initial[initial] = [word] print(words_by_initial)
使用
Pythondefaultdict(list)
:from collections import defaultdict words_by_initial = defaultdict(list) words = ['apple', 'banana', 'apricot', 'cat', 'dog'] for word in words:initial = word[0]words_by_initial[initial].append(word) # 如果 'initial' 不存在,会自动创建为空列表 [] print(words_by_initial)
这里,
list
作为默认工厂函数,当访问不存在的键时,会创建一个空列表。 -
累加/聚合 (Accumulating/Aggregating): 对某个键下的值进行累加或进行其他聚合操作。
假设你有一些销售数据,想要计算每个产品的总销售额:
Pythonfrom collections import defaultdictsales_data = [{'product': 'A', 'amount': 100},{'product': 'B', 'amount': 50},{'product': 'A', 'amount': 75},{'product': 'C', 'amount': 200},{'product': 'B', 'amount': 120}, ]total_sales = defaultdict(float) # 或者 int,根据你的数据类型for item in sales_data:product = item['product']amount = item['amount']total_sales[product] += amountprint(total_sales) # 输出: defaultdict(<class 'float'>, {'A': 175.0, 'B': 170.0, 'C': 200.0})
defaultdict
的工厂函数
defaultdict
的 default_factory
可以是任何可调用对象,而不仅仅是内置类型(如 int
、list
、set
)。
-
Pythonint
: 默认值为0
。d = defaultdict(int) print(d['a']) # 0
-
Pythonlist
: 默认值为[]
。d = defaultdict(list) d['a'].append(1) print(d) # defaultdict(<class 'list'>, {'a': [1]})
-
Pythonset
: 默认值为set()
。d = defaultdict(set) d['a'].add(1) d['a'].add(2) print(d) # defaultdict(<class 'set'>, {'a': {1, 2}})
-
Pythonstr
: 默认值为''
。d = defaultdict(str) print(d['a']) # ''
-
自定义函数:
Pythondef create_default_value():return "Not Found"d = defaultdict(create_default_value) print(d['unknown_key']) # Not Found
-
Pythonlambda
表达式:d = defaultdict(lambda: {'count': 0, 'items': []}) d['product1']['count'] += 1 d['product1']['items'].append('item_x') print(d['product1']) # {'count': 1, 'items': ['item_x']}
defaultdict
与 dict.setdefault()
的比较
在某些情况下,dict.setdefault()
也可以实现类似的功能,但 defaultdict
通常更简洁高效。
dict.setdefault(key, default_value)
方法会在字典中查找 key
。如果 key
存在,则返回其对应的值;如果 key
不存在,则将 key
插入字典并赋值为 default_value
,然后返回 default_value
。
使用 dict.setdefault()
进行计数:
Python
counts = {}
my_list = ['apple', 'banana', 'apple']
for item in my_list:counts[item] = counts.setdefault(item, 0) + 1
print(counts) # {'apple': 2, 'banana': 1}
使用 dict.setdefault()
进行分组:
Python
words_by_initial = {}
words = ['apple', 'banana', 'apricot']
for word in words:initial = word[0]words_by_initial.setdefault(initial, []).append(word)
print(words_by_initial) # {'a': ['apple', 'apricot'], 'b': ['banana']}
对比:
- 简洁性:
defaultdict
在很多情况下代码更简洁,因为它省去了显式的条件判断或setdefault
调用。 - 性能: 对于大量操作,
defaultdict
通常比重复调用setdefault
稍微快一些,因为它避免了每次访问时都可能进行的键查找和方法调用开销。 - 语义:
defaultdict
明确表达了“如果键不存在,就创建一个默认值”的意图,使得代码更易读。 - 灵活性:
defaultdict
允许你指定一个工厂函数,而setdefault
每次都需要提供一个具体的默认值。虽然setdefault
也可以结合函数调用,但不如defaultdict
的设计直接。