如何实现有序的默认字典?
我想把 OrderedDict()
和 defaultdict()
这两个东西结合在一起,做成一个新的对象,这个对象应该是一个有顺序的、默认的 dict
(字典)。
这样做可以吗?
11 个回答
40
如果你想要一个简单的解决方案,不需要用到类的话,你可以直接使用 OrderedDict.setdefault(key, default=None)
或者 OrderedDict.get(key, default=None)
。如果你只是从几个地方获取或设置值,比如在一个循环里,你可以很方便地使用 setdefault。
totals = collections.OrderedDict()
for i, x in some_generator():
totals[i] = totals.get(i, 0) + x
对于列表来说,使用 setdefault
更简单:
agglomerate = collections.OrderedDict()
for i, x in some_generator():
agglomerate.setdefault(i, []).append(x)
但是如果你使用得超过几次,可能更好还是建立一个类,就像其他回答里提到的那样。
49
这里还有一种可能性,灵感来自于Raymond Hettinger的super()考虑,在Python 2.7.X和3.4.X上进行了测试:
from collections import OrderedDict, defaultdict
class OrderedDefaultDict(OrderedDict, defaultdict):
def __init__(self, default_factory=None, *args, **kwargs):
#in python3 you can omit the args to super
super(OrderedDefaultDict, self).__init__(*args, **kwargs)
self.default_factory = default_factory
如果你查看这个类的MRO(也就是,help(OrderedDefaultDict)
),你会看到这个:
class OrderedDefaultDict(collections.OrderedDict, collections.defaultdict)
| Method resolution order:
| OrderedDefaultDict
| collections.OrderedDict
| collections.defaultdict
| __builtin__.dict
| __builtin__.object
这意味着,当一个OrderedDefaultDict
的实例被初始化时,它会先调用OrderedDict
的初始化方法,而OrderedDict
又会在调用__builtin__.dict
之前先调用defaultdict
的方法,这正是我们想要的效果。
100
下面这个(使用了一个修改过的版本,来自这个食谱)对我来说是有效的:
from collections import OrderedDict, Callable
class DefaultOrderedDict(OrderedDict):
# Source: http://stackoverflow.com/a/6190500/562769
def __init__(self, default_factory=None, *a, **kw):
if (default_factory is not None and
not isinstance(default_factory, Callable)):
raise TypeError('first argument must be callable')
OrderedDict.__init__(self, *a, **kw)
self.default_factory = default_factory
def __getitem__(self, key):
try:
return OrderedDict.__getitem__(self, key)
except KeyError:
return self.__missing__(key)
def __missing__(self, key):
if self.default_factory is None:
raise KeyError(key)
self[key] = value = self.default_factory()
return value
def __reduce__(self):
if self.default_factory is None:
args = tuple()
else:
args = self.default_factory,
return type(self), args, None, None, self.items()
def copy(self):
return self.__copy__()
def __copy__(self):
return type(self)(self.default_factory, self)
def __deepcopy__(self, memo):
import copy
return type(self)(self.default_factory,
copy.deepcopy(self.items()))
def __repr__(self):
return 'OrderedDefaultDict(%s, %s)' % (self.default_factory,
OrderedDict.__repr__(self))