UserDict类的优点是什么?

30 投票
5 回答
32048 浏览
提问于 2025-04-15 14:08

使用UserDict类有什么好处呢?

我的意思是,如果我不使用下面的代码:

class MyClass(object):
    def __init__(self):
        self.a = 0
        self.b = 0
...
m = MyClass()
m.a = 5
m.b = 7

而是写成这样:

class MyClass(UserDict):
    def __init__(self):
        UserDict.__init__(self)
        self["a"] = 0
        self["b"] = 0
...
m = MyClass()
m["a"] = 5
m["b"] = 7

补充:如果我理解得没错的话,在这两种情况下,我都可以在运行时给对象添加新字段,对吗?

m.c = "Cool"

还有

m["c"] = "Cool"

5 个回答

7

正确地覆盖 dict 是有点棘手的,但使用 UserDict 就简单多了。曾经有人讨论要把它从 Python3 中去掉,但我觉得保留它是有原因的。下面是一个例子:

class MyDict(dict):

  def __setitem__(self, key, value):
    super().__setitem__(key, value * 10)


d = MyDict(a=1, b=2)  # Oups MyDict.__setitem__ not called
d.update(c=3)  # Oups MyDict.__setitem__ not called
d['d'] = 4  # Good!
print(d)  # {'a': 1, 'b': 2, 'c': 3, 'd': 40}

UserDict 继承了 collections.abc.MutableMapping,所以没有那些缺点:

class MyDict(collections.UserDict):

  def __setitem__(self, key, value):
    super().__setitem__(key, value * 10)


d = MyDict(a=1, b=2)  # Good: MyDict.__setitem__ correctly called
d.update(c=3)  # Good: MyDict.__setitem__ correctly called
d['d'] = 4  # Good
print(d)  # {'a': 10, 'b': 20, 'c': 30, 'd': 40}
8

把字典(dict)进行子类化可以让你拥有字典的所有功能,比如可以用 if x in dict: 来检查某个值是否在字典里。通常你这么做是为了扩展字典的功能,比如创建一个有序字典。

顺便说一下,在更新版的Python中,你可以直接对子类化 dict,不需要使用 UserDict 了。

48

UserDict.UserDict 从 Python 2.2 开始就没有太大用处了,因为正如 @gs 提到的,现在你可以直接继承 dict 了。它的存在主要是为了兼容 Python 2.1 及更早的版本,那时候内置类型不能被继承。不过,它在 Python 3 中仍然保留了(现在在 collections 模块中),因为正如文档所说,

这个类的需求部分被直接从 dict 继承的能力所取代;然而,这个类可能更容易使用,因为底层的字典可以作为一个属性访问。

UserDict.DictMixin 在 Python 2 中非常实用——正如文档所说,

这个模块定义了一个混合类 DictMixin,为已经有最基本映射接口的类定义了所有字典方法。这大大简化了需要替代字典的类的编写(比如 shelve 模块)。

你可以继承它,定义一些基本方法(至少要有 __getitem__,这足以实现只读映射,不能获取键或迭代;如果需要这些功能,还要定义 keys;可能还需要 __setitem__,这样就有了读写映射,但不能删除项目;如果想要完整功能,可以加上 __delitem__,并可能为了性能重写其他方法),这样就能实现 dict 丰富的 API(如 updateget 等)。这是一个很好的 模板方法设计模式的例子。

在 Python 3 中,DictMixin 被移除了;你可以通过依赖 collections.MutableMapping 来获得 几乎 相同的功能(或者仅使用 collections.Mapping 来实现只读映射)。虽然这种方式更优雅,但使用起来没有那么方便(可以看看这个问题,它被关闭时的回复是“不会修复”;短暂的讨论值得一读)。

撰写回答