我正在使用defaultdict(set)
来填充非常大的数据结构中的内部映射。填充后,整个结构(包括映射)将暴露给客户端代码。那时,我不希望任何人修改映射。
没有人故意这样做。但有时,客户端代码可能会意外地引用不存在的元素。此时,普通字典会引发KeyError
,但由于映射是defaultdict
,它只是在该键上创建一个新元素(空集)。这很难理解,因为一切都是默默无闻的。但我需要确保不会发生这种情况(语义实际上并没有中断,但映射会变得很大)。
我该怎么办?我可以看到这些选择:
查找当前和未来客户端代码中的所有实例,其中对映射执行字典查找,并将其转换为mapping.get(k, {})
。这太可怕了。
“冻结”defaultdict
,将其转换为dict
。 (我知道它并没有真正冻结,但我相信客户端代码实际上并没有写mapping[k] = v
。)非常优雅,而且性能也很高。
将defaultdict
换行到dict
界面。这样做的优雅方式是什么?我担心性能损失可能很大(这种查找在紧密循环中大量使用)。
子类defaultdict
并添加一个“关闭”所有defaultdict
功能的方法,使其表现得像是常规dict
。这是上面3的变种,但我不确定它是否更快。如果不依赖实施细节,我不知道它是否可行。
在数据结构中使用常规dict
,重写其中的所有代码,首先检查元素是否在字典中,如果不在,则添加元素。不好。
答案 0 :(得分:33)
defaultdict
文档代表default_factory
:
如果default_factory属性为None,则会引发KeyError 密钥作为参数的异常。
如果您只是将defaultdict的default_factory设置为None
怎么办?如,
>>> d = defaultdict(int)
>>> d['a'] += 1
>>> d
defaultdict(<type 'int'>, {'a': 1})
>>> d.default_factory = None
>>> d['b'] += 2
Traceback (most recent call last):
File "<stdin>", line 1, in <module>
KeyError: 'b'
>>>
不确定这是否是最佳方法,但似乎有效。
答案 1 :(得分:1)
完成填充defaultdict后,您只需从中创建一个常规字典:
Dim myApt As AppointmentItem
常规字典当然有效冻结。
如果您的默认dict是递归默认字典,请参阅使用递归解决方案的this answer。
答案 2 :(得分:0)
你可以创建一个包含dict引用的类并阻止 setitem ()
from collections import Mapping
class MyDict(Mapping):
def __init__(self, d):
self.d = d;
def __getitem__(self, k):
return self.d[k]
def __iter__(self):
return self.__iter__()
def __setitem__(self, k, v):
if k not in self.d.keys():
raise KeyError
else:
self.d[k] = v