pin_drop当前位置:知识文库 ❯ 图文

Python defaultdict默认字典详解 - 数据分组与嵌套字典教程

一、概述

defaultdictcollections 模块中的默认字典类,它是 dict 的子类,在访问不存在的键时自动调用工厂函数生成默认值,而非引发 KeyErrordefaultdict 消除了手动检查键是否存在或使用 setdefault() 的繁琐代码,使得分组、聚合、嵌套字典等操作更加简洁优雅。


二、语法

代码示例

from collections import defaultdict

# 创建defaultdict
dd = defaultdict(default_factory)

# 常用工厂函数
dd = defaultdict(list)      # 默认值为空列表
dd = defaultdict(int)       # 默认值为0
dd = defaultdict(set)       # 默认值为空集合
dd = defaultdict(str)       # 默认值为空字符串
dd = defaultdict(lambda: "N/A")  # 自定义默认值

三、参数说明

参数 类型 说明
default_factory callable 键不存在时调用的工厂函数,返回默认值。为None时行为与普通dict相同

常用工厂函数及其默认值:

工厂函数 默认值 典型用途
list [] 分组聚合
int 0 计数器
set set() 去重分组
str "" 字符串拼接
dict {} 嵌套字典
lambda: value 自定义值 任意默认值

四、返回值

  • 构造函数返回 defaultdict 对象

  • 访问不存在的键时,返回 default_factory() 的返回值

  • default_factory属性返回当前工厂函数


五、代码示例

示例1:基本使用与对比

代码示例

from collections import defaultdict

# 普通dict的问题
data = [("水果", "苹果"), ("蔬菜", "白菜"), ("水果", "香蕉")]

# 方式1:普通dict需要判断键是否存在
groups_dict = {}
for category, item in data:
    if category not in groups_dict:
        groups_dict[category] = []
    groups_dict[category].append(item)
print(f"普通dict: {groups_dict}")

# 方式2:使用setdefault
groups_setdefault = {}
for category, item in data:
    groups_setdefault.setdefault(category, []).append(item)
print(f"setdefault: {groups_setdefault}")

# 方式3:使用defaultdict(最简洁)
groups_dd = defaultdict(list)
for category, item in data:
    groups_dd[category].append(item)
print(f"defaultdict: {dict(groups_dd)}")

输出:

代码示例

普通dict: {'水果': ['苹果', '香蕉'], '蔬菜': ['白菜']}
setdefault: {'水果': ['苹果', '香蕉'], '蔬菜': ['白菜']}
defaultdict: {'水果': ['苹果', '香蕉'], '蔬菜': ['白菜']}

示例2:不同工厂函数的应用

代码示例

from collections import defaultdict

# int工厂函数:计数器
counter = defaultdict(int)
words = ["apple", "banana", "apple", "cherry", "banana", "apple"]
for word in words:
    counter[word] += 1
print(f"计数器: {dict(counter)}")

# set工厂函数:去重分组
unique_groups = defaultdict(set)
pairs = [("A", 1), ("A", 2), ("B", 1), ("A", 1), ("B", 3)]
for key, value in pairs:
    unique_groups[key].add(value)
print(f"去重分组: {dict(unique_groups)}")

# 自定义工厂函数
config = defaultdict(lambda: "未配置")
config["host"] = "localhost"
print(f"\n已配置的键: {config['host']}")
print(f"未配置的键: {config['port']}")

输出:

代码示例

计数器: {'apple': 3, 'banana': 2, 'cherry': 1}
去重分组: {'A': {1, 2}, 'B': {1, 3}}

已配置的键: localhost
未配置的键: 未配置

示例3:嵌套defaultdict与多级分组

代码示例

from collections import defaultdict

# 嵌套defaultdict:二维分组
def nested_dict():
    """创建嵌套的defaultdict"""
    return defaultdict(list)

# 按年份和月份分组
sales_data = [
    ("2024", "01", "产品A", 100),
    ("2024", "01", "产品B", 200),
    ("2024", "02", "产品A", 150),
    ("2023", "12", "产品A", 80),
    ("2023", "12", "产品B", 120),
]

# 二级分组
sales_by_month = defaultdict(nested_dict)
for year, month, product, amount in sales_data:
    sales_by_month[year][month].append((product, amount))

# 输出结果
print("销售数据分组:")
for year in sorted(sales_by_month.keys()):
    print(f"  {year}年:")
    for month in sorted(sales_by_month[year].keys()):
        items = sales_by_month[year][month]
        total = sum(a for _, a in items)
        print(f"    {month}月: {len(items)}笔, 总额{total}")

# 多级聚合:使用defaultdict(int)汇总
yearly_totals = defaultdict(int)
for year, month, product, amount in sales_data:
    yearly_totals[year] += amount

print(f"\n年度汇总: {dict(yearly_totals)}")

输出:

代码示例

销售数据分组:
  2023年:
    12月: 2笔, 总额200
  2024年:
    01月: 2笔, 总额300
    02月: 1笔, 总额150

年度汇总: {'2024': 450, '2023': 200}

六、实际应用场景

  • 数据分组:按类别、日期、地区等维度对数据进行分组聚合

  • 计数统计:使用 defaultdict(int) 实现简单的计数器

  • 嵌套数据结构:构建多层嵌套的字典结构,无需逐层初始化


七、注意事项

注意1defaultdict 在访问不存在的键时会自动创建该键并插入默认值,这可能导致意外的键被创建。如果只想查询而不创建,应使用 get() 方法或 in 运算符。

注意2defaultdictdefault_factory 只影响 __getitem__dd[key])的行为,get() 方法不受影响,仍返回 None

注意3:嵌套 defaultdict 的序列化(如 json.dumps())可能遇到问题,因为 defaultdict 不是标准 dict。可使用 dict() 转换后再序列化。

注意4default_factoryNone 时,defaultdict 的行为与普通 dict 相同,访问不存在的键会引发 KeyError

提示:可以使用 functools.partial 或 lambda 创建更复杂的默认值工厂,如 defaultdict(partial(defaultdict, int)) 创建嵌套计数器。


八、相关方法对比

特性 defaultdict dict.setdefault() dict.get() Counter
自动创建键 是(返回0)
语法简洁度 最高 中等
默认值类型 可自定义 可自定义 固定None 固定0
副作用 创建键 创建键 不创建键 创建键
适用场景 分组/聚合 临时使用 安全查询 计数

九、小结

  • defaultdict 在访问不存在的键时自动创建默认值,消除了繁琐的存在性检查

  • 常用工厂函数:list(分组)、int(计数)、set(去重分组)

  • 嵌套 defaultdict 可构建多级数据结构,无需逐层初始化

  • 注意 __getitem__ 会创建键,查询用 get()in,序列化前用 dict() 转换


十、练习题

练习1

编写一个函数 group_by_length(words),使用 defaultdict 将单词按长度分组。

练习2

编写一个函数 build_index(docs),使用 defaultdict(set) 构建倒排索引,输入文档列表,输出词到文档编号的映射。

练习3

编写一个三级嵌套的 defaultdict,实现按"年份->月份->类别"汇总销售数据,并支持按任意层级查询汇总值。

常见问题

defaultdict和普通dict的性能有差异吗?

在大多数情况下,defaultdict 的性能略优于普通 dict 加 setdefault() 的组合,因为工厂函数的调用在 C 层完成。但当不需要自动创建默认值时,普通 dict 的 get() 方法性能最佳。

defaultdict的default_factory可以动态修改吗?

可以。defaultdictdefault_factory 是一个可读写属性,可以随时修改。例如:dd.default_factory = set 会将默认值从列表改为集合。

如何将defaultdict转换为JSON?

json.dumps() 不能直接序列化 defaultdict。需要先转换为普通字典:json.dumps(dict(dd))。对于嵌套的 defaultdict,可以使用递归转换。

defaultdict访问键时一定会创建新键吗?

是的,使用 dd[key] 访问不存在的键一定会创建该键。如果只想查询而不创建,应该使用 dd.get(key)key in dd

如何创建无限嵌套的defaultdict?

可以使用 lambda 自引用:infinite_dd = lambda: defaultdict(infinite_dd),然后 dd = defaultdict(infinite_dd)。这样可以创建任意深度的嵌套结构。

标签: defaultdict collections 默认字典 数据分组 Python教程 嵌套字典 工厂函数

本文涉及AI创作

内容由AI创作,请仔细甄别

list快速访问

上一篇: Python Counter计数器详解 - 词频统计与数据分析教程 下一篇: Python OrderedDict有序字典详解 - move_to_end与LRU缓存实现

poll相关推荐