--- fid: 20251025-134349 title: Dict Set Default Tutorial tags: dict, dict setdefault --- # Dict Set Default Tutorial (date: 2025-10-25) **难度**: 2 **时长**: 30 min ## 何时需要 Set Default ? `dict.setdefault(key, default_value)` 是一个非常有用的方法,它的核心逻辑是:**如果键存在,则返回其值;如果键不存在,则先将 `key: default_value` 插入字典,然后返回 `default_value`**。 它的“必要性”在于,它用一个原子操作完成了一个“检查-插入-返回”的常见模式,避免了代码重复和潜在的性能浪费。 ## 应用场景 以下是几个典型的应用场景: ### 场景1:按类别分组或聚合数据(最经典场景) **描述**:你有一个数据列表(例如,学生对象、交易记录),需要根据某个键(如类别、城市、部门)将它们分组。结果字典的每个键对应一个列表,列表里是同一组的所有项。 **例子** : 对学生分班数据按班级A,B,C分组。先用你能想到的方法实现,然后换成使用 `setdefault` 来实现。 ``` students = [('Alice', 'A'), ('Bob', 'B'), ('Charlie', 'A')] # 结果: {'A': ['Alice', 'Charlie'], 'B': ['Bob']} ``` 为什么需要 `setdefault` - **没有 `setdefault` 的写法**:你需要先检查键是否存在。如果不存在,先初始化一个空列表;然后再将值追加到列表中。这需要两行代码和一个 `if` 判断。 - **使用 `setdefault`**:它确保你总能拿到一个列表(无论是新创建的还是已存在的),然后直接进行追加操作。代码更简洁、直观,并且避免了在 `if` 分支中重复写初始化代码。 **类比**:就像你在整理文件,每个类别一个文件夹。拿到一个文件时,你不需要先去找“财务部”文件夹是否存在,`setdefault` 会直接给你“财务部”文件夹(如果不存在就当场创建一个空的),你只需要把文件放进去就行了。 ### 场景2:计数或累加 **描述**:你需要统计某些事件发生的频率,或者将数值累加到对应的键上。 **问题**: 统计单词频率。 ``` words = ['apple', 'banana', 'apple', 'orange'] # 结果: counter = {'apple': 2, 'banana': 1, 'orange': 1} ``` **为什么需要 `setdefault`** - 对于计数,初始值应该是 `0`。`setdefault(key, 0)` 保证了即使键是第一次出现,你也能拿到一个整数 `0`,然后直接进行 `+= 1` 操作。 - 这比先使用 `if key not in dict` 来初始化 `0` 要简洁。虽然 `collections.Counter` 或 `defaultdict` 是更专业的工具,但在不能或不想导入它们的情况下,`setdefault` 是一个很好的纯字典解决方案。 ### 场景3:构建嵌套数据结构 **描述**:你需要创建一个多层嵌套的字典,例如 `dict1[key1][key2] = value`。但在赋值之前,你必须确保 `dict1[key1]` 本身也是一个字典。 问题:构建城市-人名的嵌套字典 ``` data = [('London', 'Alice'), ('Paris', 'Bob'), ('London', 'Charlie')], # 结果: city_people = {'London': {'Alice': True, 'Charlie': True}, 'Paris': {'Bob': True}} ``` **为什么需要 `setdefault`**: - 如果没有它,代码会非常冗长且容易出错。你需要先检查外层键是否存在,如果不存在,要先将其值初始化为一个空字典 `{}`,然后才能进行内层的赋值。 - `setdefault(key1, {})` 一步到位地解决了这个问题。它返回那个(新的或已有的)内部字典,让你可以立即对其进行下一步操作(如再次使用 `setdefault` 或直接赋值)。 **例子**:统计每个城市中每个人的访问次数。结构会是 `{城市: {人名: 次数}}`。使用 `city_dict.setdefault(city, {})` 来安全地获取或创建内层字典。 ### 场景4:提供默认配置或上下文 **描述**:一个函数接收一个配置字典作为参数,但这个字典可能缺少某些非必需的配置项。你希望函数内部使用一个默认值,但又不希望修改传入的原始字典(或者在某些情况下,你允许用默认值填充原始字典)。 问题: 填充配置默认值 ``` # 填充配置默认值 user_config = {'color': 'blue'} # 用户只提供了颜色 defaults = {'color': 'red', 'size': 'medium', 'theme': 'light'} # 结果: {'color': 'blue', 'size': 'medium', 'theme': 'light'} ``` **为什么需要 `setdefault`**: - 它非常适合于“如果用户没提供,就使用这个默认值,并把它记下来”的场景。 - 与 `dict.get(key, default)` 不同,`get` 方法不会修改字典。而 `setdefault` 会在键缺失时**更新原字典**。这在某些场景下是需要的,比如你想在函数执行后,让传入的配置字典包含所有它使用过的默认值。 ## 总结 `setdefault` 的必要性 1. **原子性与简洁性**:它将“检查存在性”和“提供默认值”两个操作合并为一个原子操作,使代码更短、更可读,避免了 `if-else` 分支。 2. **避免重复代码**:你不需要在 `if` 分支里写初始化代码(如 `my_dict[key] = []`),只需要写一次处理逻辑(如 `my_dict[key].append(value)`)。 3. **原地更新**:与 `get()` 不同,它在键不存在时会**主动修改字典**,这对于构建数据结构或填充配置非常有用。 简而言之,**任何你需要“安全地”访问一个可能不存在的键,并准备在它不存在时立即为其设置一个初始值,然后基于该值进行操作(如追加、累加、嵌套赋值)的场景,都是 `setdefault` 的用武之地。**