Python setdefault:为什么默认值也会提前执行?

举报
yd_239549656 发表于 2026/11/04 09:30:38 2026/11/04
【摘要】 用可运行示例拆开参数求值与字典插入,说明已有键为何仍会触发默认值函数,以及按需创建时的单线程写法。

把“键不存在时插入默认值”理解成“键不存在时才执行默认值表达式”,很容易在 Python 的 setdefault 中踩坑。下面用一次函数调用记录,拆开这两个时机。

1. 先执行参数表达式,再调用 setdefault

calls = []

def make_default():
    calls.append("built")
    return []

data = {"tasks": ["ready"]}
result = data.setdefault("tasks", make_default())

print(result)  # ['ready']
print(calls)   # ['built']
print(data)    # {'tasks': ['ready']}

tasks 已经存在,所以字典保留原来的列表,并返回它。与此同时,make_default() 已经执行了一次。

执行顺序是:求出 make_default() 的结果,把这个结果作为第二个实参,然后调用 setdefault。字典是否插入值,是方法收到实参之后才作出的判断。如果默认值函数有计数、日志或其他副作用,即使键已存在,这些操作也会发生。

2. 返回值与插入值可以是同一个对象

data = {}
bucket = data.setdefault("tasks", [])
bucket.append("run")

print(data)                      # {'tasks': ['run']}
print(bucket is data["tasks"])   # True

键缺失时,第二个实参成为字典里的值,返回值也指向它。因此,通过 bucket 修改这个列表,字典中看到的列表会一起变化。

setdefault 依据键是否存在作判断。若已有值是 None、False、0 或空列表,它会返回该值。需要替换这些值时,应单独写清业务条件。

3. 需要按需创建时,显式检查键

下面的函数用于普通 dict 的单线程读写:

def get_or_create(mapping, key, factory):
    if key not in mapping:
        mapping[key] = factory()
    return mapping[key]

calls = []

def make_default():
    calls.append("built")
    return []

data = {"tasks": []}
existing = get_or_create(data, "tasks", make_default)
created = get_or_create(data, "new", make_default)

print(existing)                  # []
print(calls)                     # ['built']
print(created is data["new"])    # True

这里把函数本身传入 factory。已有 tasks 时,函数直接返回原列表;只有 new 缺失时,才调用一次 factory。保留空列表的判断也来自键的存在性,避免把“存在但为空”误判成“缺失”。

对于简单默认值,setdefault 可以表达简洁的插入逻辑;对于有副作用、可能抛异常或需要按需执行的初始化,把创建时机写出来更容易检查。示例没有覆盖自定义映射或并发写入,不能据此推断这些场景的行为。

验证与官方参考

本文三段代码已分别实际运行,并补充验证了已有键、缺失键、可变对象身份、空值以及默认值函数抛异常的情况。结果只说明这些最小示例的行为。

官方来源:Python 官方文档 Built-in Types 的 dict.setdefault,以及 Expressions 的 Evaluation order。可按以下路径核对原文:

  • docs.python.org/3/builtins/stdtypes.html#dict.setdefault
  • docs.python.org/3/reference/expressions.html#evaluation-order

AI 辅助说明:本文由 AI 协助组织文字与编写示例,示例已执行验证,并依据上述 Python 官方文档核对语义。

【声明】本内容来自华为云开发者社区博主,不代表华为云及华为云开发者社区的观点和立场。转载时必须标注文章的来源(华为云社区)、文章链接、文章作者等基本信息,否则作者和本社区有权追究责任。如果您发现本社区中有涉嫌抄袭的内容,欢迎发送邮件进行举报,并提供相关证据,一经查实,本社区将立刻删除涉嫌侵权内容,举报邮箱: cloudbbs@huaweicloud.com
  • 点赞
  • 收藏
  • 关注作者

评论(0)

0/1000
抱歉,系统识别当前为高风险访问,暂不支持该操作

全部回复

上滑加载中

设置昵称

在此一键设置昵称,即可参与社区互动!

*长度不超过10个汉字或20个英文字符,设置后3个月内不可修改。

*长度不超过10个汉字或20个英文字符,设置后3个月内不可修改。