这是一个双重问题,有理论部分和实用部分:
当子类化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的类?没有子类?这需要一些麻烦的样板代码,不是吗?
答案 0 :(得分:15)
你应该在子类化时调用dict.__init__(self)
;事实上,你不知道dict中究竟发生了什么(因为它是内置的),并且可能因版本和实现而异。不调用它可能会导致不正确的行为,因为你无法知道dict在哪里持有其内部数据结构。
顺便说一下,你没有告诉我们你想要做什么;如果你想要一个带有dict(映射)行为的类,并且你真的不需要一个dict(例如,你的软件中没有任何代码可以在isinstance(x, dict)
执行),你可能会更好使用UserDict.UserDict
或UserDict.DictMixin
如果您使用的是python< = 2.5,或collections.MutableMapping
,如果您使用的是python> = 2.6。这些将为你的班级提供优秀的词典行为。
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
基本类型之类的子类,则还可以考虑集合中的UserDict
。 UserDict
旨在被子类化。