Python 子类化dict:应该调用dict.\uuuu init\uuuuu()吗?

Python 子类化dict:应该调用dict.\uuuu init\uuuuu()吗?,python,subclass,dictionary,init,Python,Subclass,Dictionary,Init,这里有两个问题,一个是理论问题,一个是实践问题: 将dict子类化时: class ImageDB(dict): def __init__(self, directory): dict.__init__(self) # Necessary?? ... 是否应该调用dict.\uuuuu init\uuuuuuu(self)作为“安全”措施(例如,如果存在一些重要的非琐碎实现细节)?如果未调用dict.\uuu init\uuu(),是否存在代码与Py

这里有两个问题,一个是理论问题,一个是实践问题:

将dict子类化时:

class ImageDB(dict):
    def __init__(self, directory):
        dict.__init__(self)  # Necessary?? 
        ...
是否应该调用
dict.\uuuuu init\uuuuuuu(self)
作为“安全”措施(例如,如果存在一些重要的非琐碎实现细节)?如果未调用
dict.\uuu init\uuu()
,是否存在代码与Python未来版本中断的风险?我在这里寻找做一件事或另一件事的根本原因(实际上,调用
dict.\uuu init\uu()
是安全的)

我的猜测是,当调用
ImageDB.\uuuuu init\uuuuuuuu(self,directory)
时,self已经是一个新的空dict对象,因此不需要调用
dict.\uuuuu init\uuuuuuuuu
(我确实希望dict首先是空的)。这是正确的吗

编辑

上述基本问题背后更实际的问题如下。我考虑将dict子类化,因为我会经常使用db[…]语法(而不是一直使用db.contents[…]);对象的唯一数据(属性)实际上是一个dict。我想向数据库中添加一些方法(例如,
get\u image\u by\u name()
,或者
get\u image\u by\u code()
),并且只覆盖
\u init\u()
,因为图像数据库是由包含它的目录定义的

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

很多答案中都提到了“工厂”。所以我想这一切归结为:您是将dict子类化,重写
\uuuu init\uuuu()
并添加方法,还是编写一个(工厂)函数来返回一个dict,向其中添加方法?我倾向于选择第一种解决方案,因为factory函数返回一个对象,该对象的类型并不表示它有额外的语义和方法,但是您认为呢

编辑2

我从每个人的回答中得出结论,当新类“不是字典”时,尤其是当它的
\uuuuu init\uuuuuuuuu
方法不能采用与dict的
\uuuuuu init\uuuuuu
相同的参数时,将dict子类化不是一个好主意(这是上面“实际问题”中的情况)。换句话说,如果我理解正确,共识似乎是:当您子类化时,所有方法(包括初始化)必须与基类方法具有相同的签名。这允许isinstance(subclass_instance,dict)保证
subclass_instance.\uuu init_u()
可以像
dict.\uu init_u()
一样使用

然后又出现了另一个实际问题:除了初始化方法之外,与dict类似的类应该如何实现?没有子类化?这将需要一些麻烦的样板代码,不需要向collections模块添加一个有序的dict

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

python3.1的建议(并被接受)使用的
\uuuu init\uuuu
如下所示:

+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.\uuu init\uuu()
不需要被调用

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

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

当子类化时,您可能应该调用
dict.\uuuu init\uuuuu(self)
;事实上,您不知道dict中到底发生了什么(因为它是一个内置的),这可能因版本和实现而异。不调用它可能会导致不正确的行为,因为您不知道dict的内部数据结构存放在哪里


顺便说一下,你没有告诉我们你想做什么;如果您想要一个具有dict(映射)行为的类,并且实际上不需要dict(例如,在软件中的任何地方都没有代码执行
isinstance(x,dict)
,这是应该的),您可能更擅长使用
UserDict.UserDict
UserDict.DictMixin
如果您使用的是python您通常应该调用基类“
\uuuuu init\uuuu
,那么为什么要在这里做一个例外呢

不要重写
\uuuu init\uuuu
,或者如果需要重写
\uuuu init\uuuu
调用基类
\uuuu init\uuuu
,如果您担心参数,只需传递*args、**kwargs,或者如果您想要空的dict,则不传递任何内容,例如

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

我们不应该假设基类正在做什么或没有做什么,不调用基类
\uuu init\uu
子类化dict时要小心酸洗;例如,在2.7中需要, 在旧版本中可能是uuu getstate uuu设置state uuu。(我不知道为什么。)


如果您计划对类进行分类,如代码> DICT< /Case> BASE类型,也可以考虑从集合。code>UserDict被设计成子类。

这很有趣。现在,不使用Python 3.1调用dict.\uuu init\uuu()是安全的,但是将来呢?因为我不重写任何方法,在ImageDB中,子类化是非常安全的;只有初始化是特殊的(它构建了dict)。对不起,EOL,我没有跟上你。在我看来,Python 3.1是未来……:)考虑init实际上在做什么。它用所有参数和关键字更新dict。这是您的类必须要做的事情,因此调用dict.uuu init_uuu(self,*args,**kwds)可能会为您解决这个问题,或者您必须调用self.update,就像OrderedDict一样。@Tor Valamo:我已经为我正在寻找的功能添加了详细信息。基本上,类中包含的唯一数据是字典,我希望直接通过db[…]而不是db.contents[…]访问它。O
class MyContainer(dict):
    def newmethod1(self, args):
        pass

    def newmethod2(self, args2):
        pass


def createImageDb(directory):
    d = MyContainer()
    # fill the container
    return d
class MyDict(dict):
    def __init__(self, *args, **kwargs ):
        myparam = kwargs.pop('myparam', '')
        dict.__init__(self, *args, **kwargs )
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)