李涛自留地
回到顶部
首页 > python学习 > python中字典怎么单独加键,几种写法和踩坑记录

python中字典怎么单独加键,几种写法和踩坑记录

文章目录

上周五晚上快十一点,我在改一个抓取脚本的配置,需要往已有的字典里单独塞一个键,结果手一滑写岔了,取值全是 None。当时第一反应是字典坏了,赶紧翻文档,索性把 python中字典怎么单独加键 的几种写法从头试了一遍,顺手记成这篇,也给自己留个底。

用方括号直接赋值加键,九成场景就它

我最早学的写法就是 d["k"] = v。它做的事很直白:键不存在就新建,存在就覆盖。给配置字典补一个字段,我基本都用它,一行就完事。

树脂槽位里一个空格正被放进值块,python中字典怎么单独加键

真正要小心的是覆盖。你如果只想「有就保留、没有才补」,方括号会把你原来的值冲掉。我在日志配置上白跑过一次,level 被脚本改成 DEBUG,排查了半天才想起来是这行。

cfg = {"host": "127.0.0.1", "port": 8080}
cfg["timeout"] = 5        # 新键,直接加上
cfg["port"] = 9000        # 旧键,值被覆盖
print(cfg["port"])        # 9000

输出就一行 9000,这就是覆盖的证据。所以在写 python中字典怎么单独加键 的代码之前,我会先问自己一句:这个键可能已经存在吗。

两把同款钥匙插进同一个哈希桶格,python中字典怎么单独加键

会存在又不想动,那就别用方括号。这是我现在的习惯,比事后回滚省事得多。其实很多人踩坑都不是不会写,而是没想清楚要不要保留旧值。

还有个细节值得记一下:字典是可变对象。你把字典传进函数,在函数里用方括号加键,外面那份也看得见。我第一次碰这个,以为函数里改的是副本,结果主流程里的配置被悄悄改了。

不是 bug,是对象引用。你把 python中字典怎么单独加键 的代码写进工具函数时,要么明确它就是修改原字典,要么先 copy 一份再动手,别让调用方猜。

setdefault 和 update 的差别,第一次用容易搞混

setdefault 的逻辑是「没有才写」,这也是 python中字典怎么单独加键 里最常被误会的一个方法。键已存在时它不改值,还把旧值返回给你;键不存在才插入默认值。我第一次用它,以为它会强制写入,结果调试了半小时。

update 是批量合并。传进去的字典里每个键都往目标字典里塞,重复的键照样覆盖。你要加一批键,用它比循环赋值干净;只加一个键,用它反而绕。

d = {"a": 1}
d.setdefault("a", 99)      # 已存在,值还是 1
d.setdefault("b", 2)       # 不存在,插入 2
d.update({"b": 20, "c": 3})   # b 被改成 20
print(d)                   # {'a': 1, 'b': 20, 'c': 3}

输出大概长这样,重点看 b:setdefault 没碰它,update 把它改了。搞清楚 setdefault 和 update 的差别,你写 python中字典怎么单独加键 的时候就不太会选错。

老实说这两个方法的返回值也不一样。setdefault 返回该键对应的值,update 返回 None。把它们塞进链式调用里混用,报错信息会很难看。

成排哈希桶上方悬着终端的光带,python中字典怎么单独加键

我现在的判断很粗暴:单个键、要保留旧值,用 setdefault;单个键、要覆盖,直接赋值;一批键,才轮到 update。这三句话能覆盖我在业务里遇到的大部分情况。

嵌套字典加键,父级路径得先存在

多层字典最容易翻车,这也是 python中字典怎么单独加键 里最费神的一段。d["a"]["b"] = 1 的前提是 d["a"] 已经是个字典:d 里没有 a,抛的是 KeyError;a 在但不是字典(比如是字符串),抛的是 TypeError。我第一次写采集脚本,就在这行崩过。

稳妥的写法是逐层 setdefault,先保证外层键在,再往里写:

d = {}
d.setdefault("a", {}).setdefault("b", 1)
print(d)        # {'a': {'b': 1}}

层级深的时候这么写有点啰嗦,但比 try 包一层清楚,出问题也知道断在哪一级。

还有一种情况是键名来自变量。这时候别手拼字符串,直接用变量当下标就行,Python 不区分你给的是字面量还是变量。

层次固定又比较深的时候,我索性把路径写成列表,循环一遍逐个 setdefault。看着绕,其实是把「哪一级缺」交给循环处理,比自己写三层判断短,也不容易漏层。

批量导入时我常干的活:把一行数据整理成小字典,再 update 进主字典。键冲突的地方要先定好谁优先,不然跑完发现数据被默默覆盖,很难查。

这也是我在 python中字典怎么单独加键 上花时间最多的地方。单层写法一分钟能学会,嵌套结构里的层级判断,得靠具体业务喂出来。

键总在判断存在性,就换 defaultdict

如果你每次加键前都要判断存在性,键的类型还固定,defaultdict 更顺手。访问不存在的键时它会自动建默认值,那层判断就省了。写统计就是这么开头:from collections import defaultdict,stat = defaultdict(list),然后 stat["k"].append(1) 直接往里塞。

代价是它会「凭空造键」,连取值都会插入。这点和 get 不一样。你只想读不想写的时候,老老实实用 get,别拿 defaultdict 顶。

我踩过一次:写统计脚本时用 defaultdict(list) 累加,中途想判断某个键有没有被访问过,结果判断本身就把键造出来了。后来改成先 get 一次,逻辑才说得通。

我现在加键的顺序

拿到一个字典,第一步看键会不会重复:可能重复又要保留旧值的,走 setdefault;确定要覆盖的,方括号一把梭;一次加好几个,update。

第二步看层级:嵌套结构先补父级再写子级。这一步很容易漏,我吃过亏,报错还只指向最里面那一层,往上翻好几层才看到根。

键名来自循环或接口返回时,我会先把候选键打印一遍再写。折腾这几分钟,比跑完发现字段全是旧值划算,尤其是批量任务。

python中字典怎么单独加键 这件事说到底就三条路:赋值、setdefault、update,剩下的都是组合。判断依据只有一个:你到底要不要保留旧值。

我把这套顺序写在便签上贴过一阵,后来熟了才撕掉。你要是刚接手别人的脚本,先按这个顺序读一遍,能很快看出它想覆盖还是想补缺。

我的经验是先把「要不要覆盖」定死,再挑写法,别一边写一边改主意。先定覆盖、再挑方法、最后才想层级,这个顺序我改过两回才理顺。

若非作者标注皆为李涛自留地原创文章,转载或复制请以超链接形式并注明出处。