使用命名参数传递default_factory构造defaultdict时触发KeyError问题
default_factory会让defaultdict抛出KeyError? 这个问题的核心在于**collections.defaultdict的构造函数把default_factory设为了仅限位置参数**,不能通过关键字参数传递。
拆解两种情况的差异:
对于
c1 = collections.defaultdict(str):
这里str是作为第一个位置参数传递给default_factory的,defaultdict正确识别了这个默认工厂函数。当你访问不存在的键c1['new']时,会自动调用str()生成空字符串,所以能正常运行。对于
c2 = collections.defaultdict(default_factory=str):defaultdict的__init__方法签名是__init__(self, default_factory=None, /, **kwargs),其中的/是Python的语法标记,表示此符号之前的参数只能用位置传递,不能用关键字参数。所以你写的default_factory=str并没有被赋值给真正的默认工厂属性,而是被当成了一个普通的键值对,存入了字典的初始数据里(相当于给c2初始化了c2['default_factory'] = str)。此时c2的实际default_factory还是默认的None,和普通字典一样,访问不存在的键自然会抛出KeyError。
验证你的猜测:
把你写的测试函数改成仅限位置参数的版本,就能复现同样的问题:
def func(first, /, **kwargs): print(f"first参数值: {first}") print(f"kwargs内容: {kwargs}") # 正常运行,first接收位置参数1 func(1) # 报错!因为first是仅限位置参数,不能用关键字传递 func(first=1)
这和defaultdict的情况完全一致——你以为传递的是构造参数,实际上被当成了字典的初始键值对。
想提升可读性怎么办?
因为default_factory被设计成仅限位置参数,没办法直接用命名参数传递,但可以用两种方式兼顾可读性:
- 加注释说明:
c1 = collections.defaultdict(str) # 默认工厂函数:str,生成空字符串
- 自己封装一个小函数,把位置参数转成命名参数:
from collections import defaultdict def create_defaultdict(default_factory, **initial_data): return defaultdict(default_factory, **initial_data) # 这样就能用命名参数,可读性拉满 c3 = create_defaultdict(default_factory=str) print(c3['new']) # 输出空字符串,正常运行
内容的提问来源于stack exchange,提问作者marsl

