【问题标题】:Advantages of UserDict class?UserDict 类的优点?
【发布时间】:2009-09-08 07:00:17
【问题描述】:

使用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"

【问题讨论】:

    标签: python oop


    【解决方案1】:

    UserDict.UserDict 自 Python 2.2 以来没有实质性的附加值,因为正如 @gs 所提到的,您现在可以直接子类化 dict ——它的存在只是为了向后兼容 Python 2.1 及更早版本,当内置类型无法使用时子类。尽管如此,它仍然保留在 Python 3 中(现在在 collections 模块中的适当位置),因为正如 the docs 现在提到的那样,

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

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

    模块定义了一个mixin,DictMixin, 定义所有字典方法 已经有最低要求的班级 映射接口。这大大 简化编写需要的类 可以代替字典 (例如搁置模块)。

    您将其子类化,定义一些基本方法(至少__getitem__,这对于没有获取键或迭代能力的只读映射就足够了;如果您需要这些能力,还可以使用keys;可能__setitem__ ,并且您有一个 R/W 映射,但不能删除项目;添加 __delitem__ 以获得完整功能,并可能出于性能原因覆盖其他方法),并获得 dict 丰富 API 的完整实现(updateget 等)。 Template Method 设计模式的一个很好的例子。

    在 Python 3 中,DictMixin 消失了;您可以几乎通过依赖 collections.MutableMapping 来获得相同的功能(或者只是 collections.Mapping 用于 R/O 映射)。它更优雅一些,虽然不是很方便(参见this issue,它以“不会修复”结束;简短的讨论值得一读)。

    【讨论】:

    • 自 09 年以来,python 3 中的事情发生了一些变化,DictMixin 又回来了:docs.python.org/library/userdict.html#UserDict.DictMixin - 尽管 MutableMapping 确实建议用于大多数用途。
    • 这仍然有效吗?阅读“Fluent Python”,作者建议继承 UserDict,因为“主要警告:内置代码(用 C 编写)不会调用被用户定义的类覆盖的特殊(魔术)方法”。这本书涵盖了 3.x,但此警告是否也不适用于 2.7?
    • @Stefano:Python 3 的标准库中现在没有,而且我很确定从来没有任何 DictMixin。您提供的链接当时是指向 Python 2.7.2 文档的链接(当前为 404)。
    • @Kundor 你似乎是对的,尽管我想知道为什么我当时会发布一个 py2 文档链接。 py3中有docs.python.org/3/library/collections.html#collections.UserDict,可能是我搞糊涂了。感谢您指出这一点!
    • 我在 Python 3 官方文档 docs.python.org/3/library/collections.html#collections.UserDict 中看到了 UserDict,但没有看到任何关于 MutableMapping 的信息。很可能,答案已经过时了。
    【解决方案2】:

    将 dict 子类化为您提供 dict 的所有功能,例如 if x in dict:。如果您想扩展 dict 的功能,通常会这样做,例如创建有序 dict。

    顺便说一句:在最近的 Python 版本中,您可以直接继承 dict,而不需要 UserDict

    【讨论】:

      【解决方案3】:

      正确覆盖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}
      

      【讨论】:

        【解决方案4】:

        嗯,从 3.6 开始,肯定有一些缺点,正如我刚刚发现的那样。即,isinstance(o, dict) 返回 False。

            from collections import UserDict
        
            class MyClass(UserDict):
                pass
        
            data = MyClass(a=1,b=2)
        
            print("a:", data.get("a"))
            print("is it a dict?:", isinstance(data, dict))
        
        

        不是字典!

        a: 1
        is it a dict?: False
        

        更改为 class MyClass(dict):isinstance 返回 True。

        但是...使用 UserDict,您可以逐步实现它。

        (pdb-ing 到函数/方法是查看它们如何工作的简单方法)

        
        #assumes UserDict
        
        di = MyClass()
        
        import pdb
        
        #pdb will have work if your ancestor is UserDict, but not with dict
        #since it is c-based
        pdb.set_trace()
        di["a"]= 1
        
        

        【讨论】:

        • 虽然 UserDict 不继承自字典,但存储 UserDict 对象的字典的data 属性是普通字典,可以测试如isinstance(mydict.data, dict)
        猜你喜欢
        • 2014-10-17
        • 1970-01-01
        • 1970-01-01
        • 2011-03-07
        • 2014-07-27
        • 2011-08-02
        • 1970-01-01
        • 2015-06-21
        • 2014-03-21
        相关资源
        最近更新 更多