如何将defaultdicts [defaultdicts]的defaultdict转换为dicts [of dicts]的dict?

时间:2014-10-21 21:38:39

标签: python python-2.7 dictionary collections

使用this answer,我创建了defaultdictdefaultdict。现在,我想把那个深层嵌套的dict对象变回普通的python dict。

from collections import defaultdict

factory = lambda: defaultdict(factory)
defdict = factory()
defdict['one']['two']['three']['four'] = 5

# defaultdict(<function <lambda> at 0x10886f0c8>, {
#             'one': defaultdict(<function <lambda> at 0x10886f0c8>, {
#                 'two': defaultdict(<function <lambda> at 0x10886f0c8>, {
#                     'three': defaultdict(<function <lambda> at 0x10886f0c8>, {
#                         'four': 5})})})})

我认为这不是正确的解决方案:

import json

regdict = json.loads(json.dumps(defdict))

# {u'one': {u'two': {u'three': {u'four': 5}}}}

此外,this answer不合适,因为它不会对嵌套的字典进行递归。

3 个答案:

答案 0 :(得分:28)

您可以在树上进行递归,将每个defaultdict实例替换为由字典理解产生的字典:

def default_to_regular(d):
    if isinstance(d, defaultdict):
        d = {k: default_to_regular(v) for k, v in d.items()}
    return d

演示:

>>> from collections import defaultdict
>>> factory = lambda: defaultdict(factory)
>>> defdict = factory()
>>> defdict['one']['two']['three']['four'] = 5
>>> defdict
defaultdict(<function <lambda> at 0x103098ed8>, {'one': defaultdict(<function <lambda> at 0x103098ed8>, {'two': defaultdict(<function <lambda> at 0x103098ed8>, {'three': defaultdict(<function <lambda> at 0x103098ed8>, {'four': 5})})})})
>>> default_to_regular(defdict)
{'one': {'two': {'three': {'four': 5}}}}

答案 1 :(得分:5)

你的actually trying to do腌制你的递归defaultdict。并且你不在乎你是否在取消选择时取回dictdefaultdict

虽然有很多方法可以解决这个问题(例如,使用自己的酸洗创建一个defaultdict子类,或者使用copyreg明确覆盖默认的子类),但是有一个& #39;死了琐碎。

注意尝试时遇到的错误:

>>> pickle.dumps(defdict)
PicklingError: Can't pickle <function <lambda> at 0x10d7f4c80>: attribute lookup <lambda> on __main__ failed

你不能挑选lambda定义的功能,因为它们是匿名的,这意味着它们无法被打开。

但实际上没有理由需要lambda定义此功能。特别是,您甚至希望它是匿名的,因为您明确地给它起了一个名字。所以:

def factory(): return defaultdict(factory)

你已经完成了。

这是在行动:

>>> from collections import defaultdict
>>> def factory(): return defaultdict(factory)
>>> defdict = factory()
>>> defdict['one']['two']['three']['four'] = 5
>>> import pickle
>>> pickle.dumps(defdict)
b'\x80\x03ccollections\ndefaultdict\nq\x00c__main__\nfactory\nq\x01\x85q\x02Rq\x03X\x03\x00\x00\x00oneq\x04h\x00h\x01\x85q\x05Rq\x06X\x03\x00\x00\x00twoq\x07h\x00h\x01\x85q\x08Rq\tX\x05\x00\x00\x00threeq\nh\x00h\x01\x85q\x0bRq\x0cX\x04\x00\x00\x00fourq\rK\x05ssss.'

在其他情况下,使用lambda代替def没有充分理由会导致问题 - 您无法在运行时对内部功能进行内省,您在调试程序中会得到更糟糕的回溯等。当你想要一个固有的匿名函数或你可以在表达式中间定义的函数时使用lambda,但不要用它来保存三个字符的输入。

答案 2 :(得分:1)

一种可能性是您可以创建自己的类,可以从“defaultdict”模式切换到“vanilla”模式。对于大型词典,这会更快,更快。这是一个覆盖 getitem 方法并具有适当的实例变量来控制行为的问题。