subclassing dict:应该调用dict .__ init __()吗?

时间:2010-01-09 11:16:00

标签: python subclass dictionary init

这是一个双重问题,有理论部分和实用部分:

当子类化dict时:

class ImageDB(dict):
    def __init__(self, directory):
        dict.__init__(self)  # Necessary?? 
        ...

应该{​​{1}}被调用,就像“安全”措施一样(例如,如果有一些重要的非平凡实现细节)?如果dict.__init__(self) 被调用,那么代码是否会破坏未来版本的Python?我正在寻找做一件事或另一件事的根本原因,这里(实际上,调用dict.__init__()是安全的)。

我的猜测是,当调用dict.__init__()时,self已经是一个新的空dict对象,因此无需调用ImageDB.__init__(self, directory)(我确实希望dict为空,在第一)。这是对的吗?

修改

以上基本问题背后的实际问题如下。我在考虑继承dict,因为我会经常使用db [...]语法(而不是一直使用db.contents [...]);对象的唯一数据(属性)确实是一个字典。我想向数据库添加一些方法(例如dict.__init__get_image_by_name()),并且只覆盖get_image_by_code(),因为图像数据库是由目录定义的包含它。

总之,(实际)问题可能是:对于行为类似于字典的东西,什么是好的实现,除了它的初始化不同(它只需要一个目录名),以及它有其他方法吗?

许多答案都提到了“工厂”。所以我想这一切归结为:你是否继承dict,覆盖__init__()并添加方法,还是编写一个返回dict的(工厂)函数,你要添加方法?我倾向于选择第一个解决方案,因为工厂函数返回一个对象,其类型并不表示它有其他语义和方法,但你怎么看?

编辑2

我从每个人的回答中得知,当新类“不是字典”时,将dict子类化并不是一个好主意,特别是当它的__init__()方法不能采用与dict {{1}相同的参数时(在上面的“实际问题”中就是这种情况)。换句话说,如果我理解正确,那么共识似乎是:当您进行子类化时,所有方法(包括初始化)必须与基类方法具有相同的签名。这允许isinstance(subclass_instance,dict)保证__init__可以像__init__一样使用。

然后会出现另一个实际问题:除了初始化方法之外,应该如何实现类似dict的类?没有子类?这需要一些麻烦的样板代码,不是吗?

5 个答案:

答案 0 :(得分:15)

你应该在子类化时调用dict.__init__(self);事实上,你不知道dict中究竟发生了什么(因为它是内置的),并且可能因版本和实现而异。不调用它可能会导致不正确的行为,因为你无法知道dict在哪里持有其内部数据结构。

顺便说一下,你没有告诉我们你想要做什么;如果你想要一个带有dict(映射)行为的类,并且你真的不需要一个dict(例如,你的软件中没有任何代码可以在isinstance(x, dict)执行),你可能会更好使用UserDict.UserDictUserDict.DictMixin如果您使用的是python< = 2.5,或collections.MutableMapping,如果您使用的是python> = 2.6。这些将为你的班级提供优秀的词典行为。

编辑:我在另一条评论中读到你并没有凌驾于任何dict的方法上!然后根本没有关于子类化的意义,不要这样做。

def createImageDb(directory):
    d = {}
    # do something to fill in the dict
    return d

编辑2:你想从dict继承添加新方法,但你不需要覆盖任何方法。比一个好的选择可能是:

class MyContainer(dict):
    def newmethod1(self, args):
        pass

    def newmethod2(self, args2):
        pass


def createImageDb(directory):
    d = MyContainer()
    # fill the container
    return d

顺便问一下:您添加了哪些方法?你确定你正在创造一个好的抽象吗?也许你最好使用一个定义你需要的方法的类,并在内部使用一个“普通”的dict。

工厂功能: http://en.wikipedia.org/wiki/Factory_method_pattern

这只是一种将实例的构造委托给函数而不是覆盖/更改其构造函数的方法。

答案 1 :(得分:11)

你通常应该调用基类'__init__,为什么要在这里做例外?

要么不要覆盖__init__,要么覆盖__init__调用基类__init__,如果你担心参数只是传递* args,** kwargs或者如果你想要什么空字典例如

class MyDict(dict):
    def __init__(self, *args, **kwargs ):
        myparam = kwargs.pop('myparam', '')
        dict.__init__(self, *args, **kwargs )

我们不应该假设基类正在做什么或不做什么,不调用基类__init__

是错误的

答案 2 :(得分:3)

在子类化dict时要小心酸洗;这例如在2.7中需要__getnewargs__, 并且可能在旧版本中__getstate__ __setstate__。 (我不知道为什么。)

class Dotdict( dict ):
    """ d.key == d["key"] """

    def __init__(self, *args, **kwargs):
        dict.__init__( self, *args, **kwargs )
        self.__dict__ = self

    def __getnewargs__(self):  # for cPickle.dump( d, file, protocol=-1)
        return tuple(self)

答案 3 :(得分:2)

PEP 372处理将有序的dict添加到集合模块。

它警告说“子类化dict是一项非常重要的任务,许多实现都没有正确覆盖所有方法,这可能导致意外结果。”

python3.1的建议(和接受)patch使用__init__,如下所示:

+class OrderedDict(dict, MutableMapping):
+    def __init__(self, *args, **kwds):
+        if len(args) > 1:
+            raise TypeError('expected at most 1 arguments, got %d' % len(args))
+        if not hasattr(self, '_keys'):
+            self._keys = []
+        self.update(*args, **kwds)

基于此,看起来不需要调用dict.__init__()

编辑:如果您没有覆盖或扩展任何dict方法,那么,我同意Alan Franzoni:使用dict工厂而不是子类化:

def makeImageDB(*args,**kwargs):
   d = {}
   # modify d
   return d

答案 4 :(得分:0)

如果您打算继承dict基本类型之类的子类,则还可以考虑集合中的UserDictUserDict旨在被子类化。