Python 子类化dict:应该调用dict.\uuuu init\uuuuu()吗?
这里有两个问题,一个是理论问题,一个是实践问题: 将dict子类化时: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
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[…]访问它。Oclass 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)