将`defaultdict`暴露为常规`dict`

时间:2012-11-20 02:20:11

标签: python python-3.x wrapper defaultdict

我正在使用defaultdict(set)来填充非常大的数据结构中的内部映射。填充后,整个结构(包括映射)将暴露给客户端代码。那时,我不希望任何人修改映射。

没有人故意这样做。但有时,客户端代码可能会意外地引用不存在的元素。此时,普通字典会引发KeyError,但由于映射是defaultdict,它只是在该键上创建一个新元素(空集)。这很难理解,因为一切都是默默无闻的。但我需要确保不会发生这种情况(语义实际上并没有中断,但映射会变得很大)。

我该怎么办?我可以看到这些选择:

  1. 查找当前和未来客户端代码中的所有实例,其中对映射执行字典查找,并将其转换为mapping.get(k, {})。这太可怕了。

  2. 数据结构完全初始化后,
  3. “冻结”defaultdict,将其转换为dict。 (我知道它并没有真正冻结,但我相信客户端代码实际上并没有写mapping[k] = v。)非常优雅,而且性能也很高。

  4. defaultdict换行到dict界面。这样做的优雅方式是什么?我担心性能损失可能很大(这种查找在紧密循环中大量使用)。

  5. 子类defaultdict并添加一个“关闭”所有defaultdict功能的方法,使其表现得像是常规dict。这是上面3的变种,但我不确定它是否更快。如果不依赖实施细节,我不知道它是否可行。

  6. 在数据结构中使用常规dict,重写其中的所有代码,首先检查元素是否在字典中,如果不在,则添加元素。不好。

3 个答案:

答案 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