所以我正在编写一个扩展字典的类,该字典现在使用一种方法“dictify”将自身转换为字典。我想做的是改变它,以便在对象上调用dict()导致相同的行为,但我不知道要覆盖哪个方法。这是不可能的,还是我错过了一些完全明显的东西? (是的,我知道下面的代码不起作用,但我希望它能说明我正在尝试做什么。)
from collections import defaultdict
class RecursiveDict(defaultdict):
'''
A recursive default dict.
>>> a = RecursiveDict()
>>> a[1][2][3] = 4
>>> a.dictify()
{1: {2: {3: 4}}}
'''
def __init__(self):
super(RecursiveDict, self).__init__(RecursiveDict)
def dictify(self):
'''Get a standard dictionary of the items in the tree.'''
return dict([(k, (v.dictify() if isinstance(v, dict) else v))
for (k, v) in self.items()])
def __dict__(self):
'''Get a standard dictionary of the items in the tree.'''
print [(k, v) for (k, v) in self.items()]
return dict([(k, (dict(v) if isinstance(v, dict) else v))
for (k, v) in self.items()])
编辑:更清楚地显示问题:
>>> b = RecursiveDict()
>>> b[1][2][3] = 4
>>> b
defaultdict(<class '__main__.RecursiveDict'>, {1: defaultdict(<class '__main__.RecursiveDict'>, {2: defaultdict(<class '__main__.RecursiveDict'>, {3: 4})})})
>>> dict(b)
{1: defaultdict(<class '__main__.RecursiveDict'>, {2: defaultdict(<class '__main__.RecursiveDict'>, {3: 4})})}
>>> b.dictify()
{1: {2: {3: 4}}}
我希望dict(b)与b.dictify()
相同答案 0 :(得分:28)
您的方法没有任何问题,但这与Perl的Autovivification功能相似,后者已在Python in this question中实现。为此向@nosklo道具。
class RecursiveDict(dict):
"""Implementation of perl's autovivification feature."""
def __getitem__(self, item):
try:
return dict.__getitem__(self, item)
except KeyError:
value = self[item] = type(self)()
return value
>>> a = RecursiveDict()
>>> a[1][2][3] = 4
>>> dict(a)
{1: {2: {3: 4}}}
修改强>
正如@Rosh Oxymoron所建议的,使用__missing__
会导致更简洁的实现。需要Python&gt; = 2.5
class RecursiveDict(dict):
"""Implementation of perl's autovivification feature."""
def __missing__(self, key):
value = self[key] = type(self)()
return value
答案 1 :(得分:2)
编辑:正如ironchefpython在评论中指出的那样,实际上并没有按照我的想法行事,因为在我的示例b[1]
中仍然是RecursiveDict
。这可能仍然有用,因为你基本上得到了一个与Rob Cowie非常相似的对象,但它建立在defaultdict
上。
您可以通过覆盖__repr__
来获取您想要的行为(或非常类似的行为),请查看以下内容:
class RecursiveDict(defaultdict):
def __init__(self):
super(RecursiveDict, self).__init__(RecursiveDict)
def __repr__(self):
return repr(dict(self))
>>> a = RecursiveDict()
>>> a[1][2][3] = 4
>>> a # a looks like a normal dict since repr is overridden
{1: {2: {3: 4}}}
>>> type(a)
<class '__main__.RecursiveDict'>
>>> b = dict(a)
>>> b # dict(a) gives us a normal dictionary
{1: {2: {3: 4}}}
>>> b[5][6] = 7 # obviously this won't work anymore
Traceback (most recent call last):
File "<stdin>", line 1, in <module>
KeyError: 5
>>> type(b)
<type 'dict'>
有可能有更好的方法来获得defaultdict
的正常字典视图而不是dict(self)
,但我找不到一个,如果你知道如何评论。
答案 2 :(得分:2)
你想要打印它就像一个字典吗?用这个:
from collections import defaultdict
class RecursiveDict(defaultdict):
'''
A recursive default dict.
>>> a = RecursiveDict()
>>> a[1][2][3] = 4
>>> a.dictify()
{1: {2: {3: 4}}}
>>> dict(a)
{1: {2: {3: 4}}}
'''
def __init__(self):
super(RecursiveDict, self).__init__(RecursiveDict)
def dictify(self):
'''Get a standard dictionary of the items in the tree.'''
return dict([(k, (v.dictify() if isinstance(v, dict) else v))
for (k, v) in self.items()])
def __dict__(self):
'''Get a standard dictionary of the items in the tree.'''
print [(k, v) for (k, v) in self.items()]
return dict([(k, (dict(v) if isinstance(v, dict) else v))
for (k, v) in self.items()])
def __repr__(self):
return repr(self.dictify())
也许您正在寻找__missing__
:
class RecursiveDict(dict):
'''
A recursive default dict.
>>> a = RecursiveDict()
>>> a[1][2][3] = 4
>>> a
{1: {2: {3: 4}}}
>>> dict(a)
{1: {2: {3: 4}}}
'''
def __missing__(self, key):
self[key] = self.__class__()
return self[key]
答案 3 :(得分:2)
你不能这样做。
我删除了之前的回答,因为我在查看源代码后发现,如果您在dict(d)
的{{1}}上调用d
,则会快速C中底层哈希的副本,并返回一个新的dict对象。
对不起。
如果您确实需要此行为,则需要创建一个不从dict
继承的RecursiveDict
类,并实现dict
接口。
答案 4 :(得分:1)
您需要覆盖__iter__
。
def __iter__(self):
return iter((k, (v.dictify() if isinstance(v, dict) else v))
for (k, v) in self.items())
而不是self.items()
,您应该在Python 2上使用self.iteritems()
。
编辑:好的,这似乎是你的问题:
>>> class B(dict): __iter__ = lambda self: iter(((1, 2), (3, 4)))
...
>>> b = B()
>>> dict(b)
{}
>>> class B(list): __iter__ = lambda self: iter(((1, 2), (3, 4)))
...
>>> b = B()
>>> dict(b)
{1: 2, 3: 4}
因此,如果您调用dict()
的对象是dict的子类,则此方法不起作用。
编辑2:要清楚,defaultdict
是dict
的子类。 dict(a_defaultdict)仍然是一个无操作。
答案 5 :(得分:0)
一旦你的dictify功能正常工作
dict = dictify
更新: 这是递归字典的简短方法:
>>> def RecursiveDict():
... return defaultdict(RecursiveDict)
然后你可以:
d[1][2][3] = 5
d[1][2][4] = 6
>>> d
defaultdict(<function ReturnsRecursiveDict at 0x7f3ba453a5f0>, {1: defaultdict(<function ReturnsRecursiveDict at 0x7f3ba453a5f0>, {2: defaultdict(<function ReturnsRecursiveDict at 0x7f3ba453a5f0>, {3: 5, 4: 6})})})
我没有看到一种巧妙的方式来实现思想。