讲师中心 微信公众号

工具推荐

视频效率加速
Laper
Laper

剧本策划到成片协作

AniShort
AniShort

AI短剧协同制作平台

Seko
Seko

AI短剧一键生成成片

updream

一键生成高质量推广成片

python中defaultdict怎么使用?

小杰酱_9499

小杰酱_9499

发布时间:2025-09-14 08:55:01

|

1060人浏览过

|

来源于php中文网

原创

defaultdict是dict的子类,访问不存在的键时自动创建默认值,避免KeyError。它通过指定工厂函数(如int、list、set或lambda)生成默认值,常用于计数、分组和构建复杂数据结构。相比普通dict的get()或if/else,defaultdict代码更简洁,尤其适合累加和追加操作。工厂函数必须无参数且每次调用生成新对象,确保可变类型独立。高级用法包括嵌套defaultdict实现多级分组,但需注意意外添加键、类型不统一及序列化问题,使用时应权衡场景以避免副作用。

python中defaultdict怎么使用?

defaultdict
在 Python 中,是
dict
的一个非常实用的子类,它最核心的功能在于,当你尝试访问一个不存在的键时,它不会像普通字典那样抛出
KeyError
,而是会自动为这个键创建一个默认值。这极大地简化了需要对缺失键进行初始化的场景下的代码。

解决方案

在使用

defaultdict
时,你需要从
collections
模块导入它,并在创建实例时提供一个“工厂函数”(factory function)。这个工厂函数会在每次遇到缺失的键时被调用,生成对应的默认值。

from collections import defaultdict

# 1. 计数场景:使用 int 作为工厂函数,默认值是 0
# 比如,我想统计一个列表中每个元素的出现次数
data = ['apple', 'banana', 'apple', 'orange', 'banana', 'apple']
counts = defaultdict(int)
for item in data:
    counts[item] += 1
print(f"计数结果: {counts}")
# 输出: defaultdict(<class 'int'>, {'apple': 3, 'banana': 2, 'orange': 1})

# 2. 分组场景:使用 list 作为工厂函数,默认值是空列表
# 比如,我想把一系列数字按奇偶分组
numbers = [1, 2, 3, 4, 5, 6, 7, 8, 9]
grouped_numbers = defaultdict(list)
for num in numbers:
    if num % 2 == 0:
        grouped_numbers['even'].append(num)
    else:
        grouped_numbers['odd'].append(num)
print(f"分组结果: {grouped_numbers}")
# 输出: defaultdict(<class 'list'>, {'odd': [1, 3, 5, 7, 9], 'even': [2, 4, 6, 8]})

# 3. 构建图结构:使用 set 作为工厂函数,默认值是空集合
# 比如,表示一个无向图的邻接列表
graph = defaultdict(set)
edges = [('A', 'B'), ('A', 'C'), ('B', 'D'), ('C', 'D'), ('D', 'A')]
for u, v in edges:
    graph[u].add(v)
    graph[v].add(u) # 无向图,所以两边都要加
print(f"图结构: {graph}")
# 输出: defaultdict(<class 'set'>, {'A': {'C', 'B', 'D'}, 'B': {'A', 'D'}, 'C': {'A', 'D'}, 'D': {'C', 'B', 'A'}})

# 4. 使用 lambda 表达式作为工厂函数,提供更复杂的默认值
# 比如,每个新键的默认值是一个包含 'default' 字符串的列表
complex_defaults = defaultdict(lambda: ['default'])
complex_defaults['key1'].append('value1')
print(f"复杂默认值: {complex_defaults}")
# 输出: defaultdict(<function <lambda> at 0x...>, {'key1': ['default', 'value1']})

你看,它的用法其实非常直观。核心就是你告诉它,当键不存在时,给我一个什么样的新东西。

为什么在某些场景下,
defaultdict
比普通
dict
加
get()
或
if/else
更优?

说实话,我个人觉得

defaultdict
最吸引人的地方在于它能让代码变得更“干净”。你不需要每次都写那些重复的检查逻辑:
if key not in my_dict: my_dict[key] = initial_value
或者
my_dict.get(key, initial_value)
。虽然
get()
方法也能处理缺失键,但它返回的是一个值,如果你需要修改这个值(比如列表的
append
或数字的
+=
),你通常还是得先获取,再赋值回去,或者干脆用
if/else
结构。

立即学习“Python免费学习笔记(深入)”;

举个例子,统计单词频率:

使用普通

dict
的方式:

words = ['apple', 'banana', 'apple', 'orange']
word_counts = {}
for word in words:
    if word in word_counts:
        word_counts[word] += 1
    else:
        word_counts[word] = 1
# 或者用 get()
# word_counts[word] = word_counts.get(word, 0) + 1
print(f"普通dict计数: {word_counts}")

使用

defaultdict
的方式:

from collections import defaultdict
words = ['apple', 'banana', 'apple', 'orange']
word_counts_default = defaultdict(int)
for word in words:
    word_counts_default[word] += 1
print(f"defaultdict计数: {word_counts_default}")

很明显,

defaultdict
的版本少了一层条件判断,代码行数更少,意图也更清晰。它把“如果键不存在就初始化”这个逻辑内化了,让你的核心业务逻辑(这里是
+= 1
)可以更流畅地表达。这种简洁性在处理大量数据分组、聚合或者构建复杂数据结构时,能显著提升开发效率和代码可读性。当然,如果你的逻辑本身就需要区分键是否存在的情况,那
defaultdict
可能就不是最好的选择,但对于常见的累加、追加操作,它简直是神来之笔。

defaultdict
的工厂函数可以是哪些类型?有哪些需要注意的地方?

defaultdict
的工厂函数,其实可以是任何“无参数可调用对象”(callable that takes no arguments)。这意味着它可以是:

  1. 内置类型构造函数:

    JSON Toolkit
    JSON Toolkit

    JSON文件多功能工具。支持格式化、验证、压缩、键排序及点记法路径查询。零依赖。

    下载
    • int
      :默认值是
      0
      。
    • list
      :默认值是
      []
      (空列表)。
    • set
      :默认值是
      set()
      (空集合)。
    • str
      :默认值是
      ''
      (空字符串)。
    • float
      :默认值是
      0.0
      。
    • dict
      :默认值是
      {}
      (空字典)。 这些是最常见的,也是最实用的。
  2. 自定义函数或

    lambda
    表达式: 你可以定义一个自己的函数,或者使用
    lambda
    表达式来返回任何你想要的默认值。

    def create_default_value():
        return {'status': 'new', 'data': []}
    
    my_complex_default = defaultdict(create_default_value)
    my_complex_default['task1']['data'].append('item1')
    print(f"自定义函数默认值: {my_complex_default}")
    
    my_lambda_default = defaultdict(lambda: '未知')
    print(f"lambda默认值: {my_lambda_default['non_existent_key']}")

需要注意的地方:

  • 无参数调用:工厂函数在被
    defaultdict
    调用时,不会接收任何参数。如果你尝试传入一个需要参数的函数,比如
    defaultdict(dict.fromkeys)
    ,那就会报错。这是它设计上的一个核心点。
  • 每次都创建新对象:当访问一个不存在的键时,工厂函数会被调用,并且每次都会创建一个全新的默认值对象。这一点对于
    list
    、
    set
    、
    dict
    这样的可变类型尤其重要。这意味着
    my_dict['key1']
    得到的空列表和
    my_dict['key2']
    得到的空列表是两个完全独立的列表对象,它们互不影响。这与 Python 函数默认参数的陷阱(所有调用共享同一个可变默认对象)是相反的,在这里是安全的。
  • 工厂函数的副作用:如果你的工厂函数有副作用(比如打印信息、修改全局变量),那么每次访问缺失键时,这些副作用都会发生。这通常不是我们期望的,所以工厂函数最好是纯粹的,只负责返回默认值。
  • 不要将
    None
    作为工厂函数
    :虽然
    None
    是一个可调用对象(
    callable(None)
    返回
    False
    ),但如果你尝试
    defaultdict(None)
    ,它会抛出
    TypeError
    。这是因为
    defaultdict
    期望一个能被实际调用的对象。

理解这些,能让你更灵活、更安全地使用

defaultdict
。

在实际项目中,
defaultdict
有哪些高级用法和潜在的陷阱?

在实际项目里,

defaultdict
的应用远不止上面那些基础例子,它能帮我们解决不少数据处理的痛点,但同时也有一些需要留心的“坑”。

高级用法:

  1. 嵌套

    defaultdict
    实现多级分组: 这是我个人觉得最酷的用法之一。想象一下,你要按年份、再按月份来分组数据。

    from collections import defaultdict
    data_points = [
        {'year': 2023, 'month': 1, 'value': 10},
        {'year': 2023, 'month': 2, 'value': 20},
        {'year': 2024, 'month': 1, 'value': 15},
        {'year': 2023, 'month': 1, 'value': 5},
    ]
    # lambda: defaultdict(list) 意思是:如果第一层键不存在,默认值是一个新的 defaultdict,这个新的 defaultdict 的默认值是 list
    yearly_monthly_data = defaultdict(lambda: defaultdict(list))
    
    for item in data_points:
        year = item['year']
        month = item['month']
        yearly_monthly_data[year][month].append(item['value'])
    
    print(f"多级分组数据: {yearly_monthly_data}")
    # 输出: defaultdict(<function <lambda> at 0x...>, {2023: defaultdict(<class 'list'>, {1: [10, 5], 2: [20]}), 2024: defaultdict(<class 'list'>, {1: [15]})})

    这种结构在处理日志分析、用户行为统计等场景下非常高效。

  2. 用作轻量级缓存或延迟计算: 如果你的默认值计算成本较高,并且希望只在需要时才计算,

    defaultdict
    可以配合自定义函数实现一个简单的按需计算机制。

    import time
    
    def expensive_computation(key):
        print(f"正在为键 '{key}' 执行耗时计算...")
        time.sleep(1) # 模拟耗时操作
        return f"计算结果 for {key}"
    
    # 注意这里不能直接传 expensive_computation,因为它需要一个 key 参数
    # 所以我们用 lambda 包裹一下,让它变成无参数调用
    cached_results = defaultdict(lambda: expensive_computation(list(cached_results.keys())[-1] if cached_results else "default_key"))
    # 上面这个 lambda 表达式有点复杂,因为它试图获取当前 defaultdict 中最后一个键来传递给 expensive_computation。
    # 更常见且安全的方式是,如果 expensive_computation 真的需要 key,那 defaultdict 就不是最直接的方案,
    # 或者让 factory 返回一个能“记住”key 的闭包。
    # 实际上,如果工厂函数需要 key,defaultdict 就不太适合。
    # 更实际的用法是:工厂函数返回一个 *固定* 的默认值,或者一个可以 *后续* 填充的结构。
    
    # 让我们换一个更实际的延迟计算例子,默认值是一个可以被填充的空列表
    lazy_data = defaultdict(list)
    # 假设我们后续会填充数据,但初始访问时是空列表
    lazy_data['user_activity'].append('login')
    print(f"延迟数据: {lazy_data}")

    这个例子有点跑偏了,因为

    defaultdict
    的工厂函数确实不能接收键。一个更符合其设计理念的“延迟计算”场景可能是:工厂函数返回一个
    None
    或者一个占位符,然后你再手动填充。不过,对于真正需要键来计算默认值的场景,通常会用
    dict.setdefault()
    或自定义
    __missing__
    方法。

潜在的陷阱:

  1. 意外的键添加: 这是最常见的“坑”。当你仅仅是想检查一个键是否存在,或者想看看它的值是什么,但这个键恰好不存在时,

    defaultdict
    会默默地添加这个键,并赋予它默认值。

    my_data = defaultdict(int)
    print(f"字典初始状态: {my_data}") # {}
    _ = my_data['non_existent_key'] # 访问,键被添加
    print(f"访问后字典状态: {my_data}") # {'non_existent_key': 0}

    如果你期望的是一个只读的字典,或者不希望字典结构被随意修改,这可能会导致一些难以察觉的副作用。在这种情况下,使用普通

    dict
    的
    get()
    方法(它不会添加键)或者
    key in my_dict
    的判断会更安全。

  2. 默认值的类型不匹配: 有时候,你可能希望某个键的值是一个

    int
    ,但另一个键的值是一个
    list
    。
    defaultdict
    只能指定一个统一的工厂函数。

    # 比如,你想统计单词,也想记录出现过的句子
    # 这是做不到的,因为 defaultdict(int) 只能处理 int 类型的默认值
    # 你不能让它在需要时返回 int,在需要时返回 list
    # word_stats = defaultdict(int) # 或者 defaultdict(list)
    # 这时候你就需要用普通 dict,或者更复杂的结构

    遇到这种需求,你可能需要一个普通的

    dict
    ,或者使用
    defaultdict(lambda: {'count': 0, 'sentences': []})
    这种更复杂的默认值结构来包裹不同类型的数据。

  3. 序列化问题: 当你尝试用

    json.dumps()
    这样的方法去序列化一个
    defaultdict
    对象时,会发现它并不会直接变成一个普通的 JSON 对象。你需要先把它转换成普通的
    dict
    。

    import json
    my_dd = defaultdict(int)
    my_dd['a'] = 1
    my_dd['b'] = 2
    # print(json.dumps(my_dd)) # 会报错:TypeError: Object of type defaultdict is not JSON serializable
    print(json.dumps(dict(my_dd))) # 正确的做法
    # 输出: {"a": 1, "b": 2}

    这是因为

    defaultdict
    内部有一些额外的元信息,JSON 编码器不知道如何处理。

总的来说,

defaultdict
是一个非常强大的工具,能让我们的代码更简洁、更优雅。但理解它的工作原理,特别是它如何处理缺失键和默认值的创建,对于避免一些潜在的问题至关重要。用得好,它能帮你省去不少麻烦;用不好,也可能引入一些不易察觉的 bug。所以,在使用它之前,多想一步,确认它真的符合你的需求,总是没错的。

热门AI工具

更多
UP简历
UP简历 Hot

一款AI办公效率工具,主要用于基于AI技术的免费在线简历制作工具,适合需要提升相关任务效率的用户。

WorkBuddy

一款AI办公效率工具,主要用于腾讯云推出的AI原生桌面智能体工作台,适合需要提升相关任务效率的用户。

DeepSeek

DeepSeek是一款面向对话、写作、编程和推理场景的AI大模型工具。

SkildArt
SkildArt Hot

SkildArt是一款AI文本写作工具,一站式 AI 视觉创作平台。

Laper
Laper Hot

Laper是专为编剧、导演和制片人推出的 AI 原生剧本创作工具。

蛙蛙写作

一款AI论文写作工具,主要用于超级AI智能写作助手,适合需要提升相关任务效率的用户。

豆包大模型

豆包大模型是一款由字节跳动推出的企业级大语言模型服务平台。

PixPix
PixPix Hot

PixPix是一款面向电商视觉生产的AI商品图生成工具。

函子Zion

一款支持AI搭建、可视化编辑和多端发布的全栈无代码开发平台。

相关专题

更多
json数据格式
json数据格式

JSON是一种轻量级的数据交换格式。本专题为大家带来json数据格式相关文章,帮助大家解决问题。

2355

2023.08.07

json是什么
json是什么

JSON是一种轻量级的数据交换格式,具有简洁、易读、跨平台和语言的特点,JSON数据是通过键值对的方式进行组织,其中键是字符串,值可以是字符串、数值、布尔值、数组、对象或者null,在Web开发、数据交换和配置文件等方面得到广泛应用。本专题为大家提供json相关的文章、下载、课程内容,供大家免费下载体验。

3862

2023.08.23

jquery怎么操作json
jquery怎么操作json

操作的方法有:1、“$.parseJSON(jsonString)”2、“$.getJSON(url, data, success)”;3、“$.each(obj, callback)”;4、“$.ajax()”。更多jquery怎么操作json的详细内容,可以访问本专题下面的文章。

1276

2023.10.13

go语言处理json数据方法
go语言处理json数据方法

本专题整合了go语言中处理json数据方法,阅读专题下面的文章了解更多详细内容。

4479

2025.09.10

css中float用法
css中float用法

css中float属性允许元素脱离文档流并沿其父元素边缘排列,用于创建并排列、对齐文本图像、浮动菜单边栏和重叠元素。想了解更多float的相关内容,可以阅读本专题下面的文章。

7707

2024.04.28

C++中int、float和double的区别
C++中int、float和double的区别

本专题整合了c++中int和double的区别,阅读专题下面的文章了解更多详细内容。

904

2025.10.23

counta和count的区别
counta和count的区别

Count函数用于计算指定范围内数字的个数,而CountA函数用于计算指定范围内非空单元格的个数。本专题为大家提供相关的文章、下载、课程内容,供大家免费下载体验。

3928

2023.11.20

counta和count的区别
counta和count的区别

Count函数用于计算指定范围内数字的个数,而CountA函数用于计算指定范围内非空单元格的个数。本专题为大家提供相关的文章、下载、课程内容,供大家免费下载体验。

3928

2023.11.20

Go-Zero API开发使用教程
Go-Zero API开发使用教程

本专题聚焦Go-Zero的API开发,拆解.api描述文件的语法结构、路由与分组定义、参数绑定与校验规则,说明统一响应体、JWT鉴权、跨域与接口文档生成的落地方式,并给出网关聚合与接口测试的实践建议。

40

2026.11.12

热门下载

更多
网站特效
/
网站源码
/
网站素材
/
前端模板

精品课程

更多
相关推荐
/
热门推荐
/
最新课程
关于我们 免责申明 举报中心 意见反馈 讲师合作 广告合作 最新更新
php中文网:公益在线php培训,帮助PHP学习者快速成长!
关注服务号
PHP中文网订阅号
每天精选资源文章推送

Copyright 2014-2026 https://www.php.cn/ All Rights Reserved | php.cn