Python字典中setdefault与get的区别及使用场景对比

半杯拿铁
半杯拿铁
发布时间:2026-11-03 | 8人浏览过
原创

详解Python字典方法setdefault与get的核心区别,包括返回值、对原字典的影响、典型使用场景,并通过代码示例和对比图帮助读者选择合适的方法。

dict.get() 和 dict.setdefault() 都可以在访问字典键时提供默认值,但关键区别在于:get() 只读取,不改变字典;setdefault() 在键不存在时会把默认值写入字典。理解这一点,就能在不同场景下做出正确选择。下面从基本行为、差异对比、实际用法到选择依据,逐步展开说明。

get方法的基本行为

dict.get(key, default=None) 尝试返回 key 对应的值。如果 key 不存在,返回 default 参数指定的值(默认为 None),字典本身不会发生任何变化。

config = {"host": "localhost", "port": 8080}

# 键存在,返回对应值
host = config.get("host", "0.0.0.0")
print(host)       # localhost

# 键不存在,返回默认值
timeout = config.get("timeout", 30)
print(timeout)    # 30

# 字典没有变化
print(config)     # {'host': 'localhost', 'port': 8080}
localhost
30
{'host': 'localhost', 'port': 8080}

可以看到,get() 返回了默认值 30,但 config 中并没有新增 "timeout" 这个键。这正是 get() 的设计意图——安全地读取,不产生副作用。

setdefault方法的基本行为

dict.setdefault(key, default=None) 的逻辑分两种情况:如果 key 已存在,直接返回已有值,不做修改;如果 key 不存在,先将 key: default 写入字典,再返回 default。

config = {"host": "localhost", "port": 8080}

# 键存在,返回已有值,不覆盖
host = config.setdefault("host", "0.0.0.0")
print(host)       # localhost

# 键不存在,写入默认值并返回
timeout = config.setdefault("timeout", 30)
print(timeout)    # 30

# 字典已新增 timeout
print(config)     # {'host': 'localhost', 'port': 8080, 'timeout': 30}
localhost
30
{'host': 'localhost', 'port': 8080, 'timeout': 30}

与 get() 不同,setdefault() 会在键缺失时真正修改字典。这在需要"确保键存在"的场景中特别有用。

核心区别对比

两个方法在调用形式上很相似,容易混淆。下面从几个维度逐一对比,帮助形成清晰的判断。

Python字典中setdefault与get的区别及使用场景对比
图1:get() 与 setdefault() 在不同场景下的行为对比

两者在"键已存在"时行为完全一致,区别只在键不存在的那一刻:get() 不碰字典,setdefault() 会把默认值填进去。

get的典型使用场景

安全取值,避免 KeyError

最常见的用法是用 get() 替代直接下标访问,防止键不存在时抛出 KeyError:

user = {"name": "张三", "age": 28}

# 直接访问不存在的键会报错
# print(user["email"])  # KeyError: 'email'

# get 安全取值
email = user.get("email", "未提供")
print(email)  # 未提供

条件逻辑中的临时替代值

当你只需要一个替代值来参与判断或计算,不希望改变原始数据时,get() 是最合适的选择:

scores = {"math": 92, "english": 85}

# 缺失科目按0分计算总分,但不在字典里添加记录
total = scores.get("math", 0) + scores.get("english", 0) + scores.get("physics", 0)
print(total)    # 177
print(scores)   # {'math': 92, 'english': 85}  — 没有 physics

从嵌套字典中安全提取数据

处理API返回的JSON数据时,多层 get() 可以避免中间某层为 None 时出错:

response = {"data": {"user": {"name": "李四"}}}

# 逐层安全取值
name = response.get("data", {}).get("user", {}).get("name", "匿名")
print(name)  # 李四

# 如果某一层缺失
response2 = {"data": {}}
name2 = response2.get("data", {}).get("user", {}).get("name", "匿名")
print(name2)  # 匿名

setdefault的典型使用场景

分组归类——按键收集列表

这是 setdefault() 最经典的用法。在遍历数据、按某个字段分组时,需要为每个新分组自动创建空列表:

records = [
    {"dept": "技术部", "name": "王五"},
    {"dept": "市场部", "name": "赵六"},
    {"dept": "技术部", "name": "钱七"},
    {"dept": "市场部", "name": "孙八"},
    {"dept": "人事部", "name": "周九"},
]

groups = {}
for r in records:
    groups.setdefault(r["dept"], []).append(r["name"])

print(groups)
# {'技术部': ['王五', '钱七'], '市场部': ['赵六', '孙八'], '人事部': ['周九']}
{'技术部': ['王五', '钱七'], '市场部': ['赵六', '孙八'], '人事部': ['周九']}

这里 setdefault(r["dept"], []) 完成了两件事:如果部门键不存在,插入空列表并返回它;如果已存在,直接返回已有列表。紧接着 .append() 往这个列表里添加姓名。如果用 get(),则无法在同一行内完成这个操作,因为 get() 返回的列表不会被字典记住。

配置合并——只填充缺失项

当你有一组默认配置,需要与用户传入的配置合并,但不能覆盖用户已有的设置时:

user_config = {"theme": "dark", "lang": "zh-CN"}

defaults = {"theme": "light", "lang": "en", "font_size": 14, "show_line_num": True}

for key, value in defaults.items():
    user_config.setdefault(key, value)

print(user_config)
# {'theme': 'dark', 'lang': 'zh-CN', 'font_size': 14, 'show_line_num': True}
{'theme': 'dark', 'lang': 'zh-CN', 'font_size': 14, 'show_line_num': True}

用户指定的 theme 和 lang 保持不变,只有缺失的 font_size 和 show_line_num 被填入默认值。

构建字典中的嵌套结构

在逐步构建复杂嵌套字典时,setdefault() 可以避免反复判断中间层是否存在:

data = {}

# 逐层确保键存在
data.setdefault("server", {}).setdefault("database", {})["host"] = "127.0.0.1"
data.setdefault("server", {}).setdefault("database", {})["port"] = 3306

print(data)
# {'server': {'database': {'host': '127.0.0.1', 'port': 3306}}}
Python字典中setdefault与get的区别及使用场景对比
图2:setdefault 在分组归类场景中的执行流程

容易混淆的地方

用 get 替代 setdefault 做分组会怎样?

有些初学者会写出类似下面的代码:

# 错误示范
groups = {}
for r in records:
    groups.get(r["dept"], []).append(r["name"])

print(groups)  # {}  — 空的!

问题在于 get() 每次返回一个新的空列表,但没有将它存入 groups,所以 append() 操作的列表立即被丢弃。这是 get() 和 setdefault() 最容易踩的坑。

setdefault 不会覆盖已有值

这是另一个常见误解。setdefault() 只在键不存在时写入,已有的值不会被替换:

d = {"color": "red"}
d.setdefault("color", "blue")
print(d["color"])  # red  — 仍然是原来的值

如果你的目的是"无论如何都设置这个值",应该直接赋值 d["color"] = "blue"。

setdefault 与 collections.defaultdict

setdefault() 与 defaultdict 在分组场景中作用类似,但有几点区别:

  • defaultdict 在任何键访问时都会自动创建默认值(包括意外访问),setdefault() 只在你显式调用时才触发
  • defaultdict 的默认值类型在创建时确定,setdefault() 可以针对不同键给不同的默认值
  • 如果只在某一处需要分组,setdefault() 更轻量;如果整个字典都需要自动初始化,defaultdict 更方便
Python字典中setdefault与get的区别及使用场景对比
图3:两种方法的执行逻辑流程对比

如何选择:一个简单的判断标准

在写代码时,可以问自己一个问题:这次访问之后,我是否需要字典里"记住"这个默认值?

  • 如果答案是不需要——只是临时取个值用一下,用 get()
  • 如果答案是需要——后续代码还会依赖这个键的存在,用 setdefault()

举几个具体例子帮助判断:

场景选择原因
显示用户昵称,没有则显示"游客"get()只是展示,不需要把"游客"存进数据
统计单词频次,首次出现时初始化为0setdefault()后续要在此基础上累加
读取配置中的超时时间,缺失时用默认值30get()不应该修改原始配置数据
将日志按等级分组到不同列表setdefault()需要字典持续积累分组结果
合并默认配置到用户配置中setdefault()需要把缺失的默认值写入用户配置

综合示例:在同一段代码中使用两者

下面的例子模拟处理一批学生考试成绩,展示在同一个任务中如何自然地混合使用两种方法:

exam_results = [
    {"name": "张三", "subject": "数学", "score": 92},
    {"name": "李四", "subject": "数学", "score": 85},
    {"name": "张三", "subject": "英语", "score": 78},
    {"name": "王五", "subject": "数学", "score": 96},
    {"name": "李四", "subject": "英语", "score": 91},
]

# 按学生姓名分组(需要持久化 → setdefault)
student_scores = {}
for result in exam_results:
    student_scores.setdefault(result["name"], []).append(
        {"subject": result["subject"], "score": result["score"]}
    )

# 生成报告时安全取值(只读 → get)
for name, scores in student_scores.items():
    math_score = None
    for s in scores:
        if s["subject"] == "数学":
            math_score = s["score"]
            break

    # 没有数学成绩时显示"未参加",但不写入原数据
    display_math = math_score if math_score is not None else "未参加"
    print(f"{name}:数学 {display_math}")
张三:数学 92
李四:数学 85
王五:数学 96

分组阶段用 setdefault(),因为需要字典记住每个学生的成绩列表;展示阶段用安全取值逻辑,因为缺失数据只需显示替代文本,不应改动原始数据。

Python字典中setdefault与get的区别及使用场景对比
图4:选择 get 还是 setdefault 的决策流程
小结:get() 是只读的安全取值,setdefault() 是"读取+按需初始化"的组合操作。两者不是替代关系,而是各有明确的适用场景。记住一个判断原则:如果你只是需要一个临时的替代值,用 get();如果你需要字典在这次操作后确实拥有这个键,用 setdefault()。

本站声明

本文内容仅供参考,不代表本站观点。转载或引用请注明出处。如发现内容问题,请通过联系我们页面反馈。

Python 相关产品和应用

全部下载

专题列表