如何将嵌套的defaultdict转为嵌套的dict?

34 投票
2 回答
11559 浏览
提问于 2025-04-28 21:53

根据这个回答,我创建了一个包含多个defaultdictdefaultdict。现在,我想把这个层层嵌套的字典对象转换回普通的Python字典。

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}}}}

另外,这个回答也不够好,因为它没有处理嵌套的字典。

暂无标签

2 个回答

7

你实际上想做的是把你的递归 defaultdict 保存起来。而且你并不在乎在恢复的时候得到的是 dict 还是 defaultdict

虽然有很多方法可以解决这个问题(比如,创建一个有自己保存功能的 defaultdict 子类,或者用 copyreg 明确覆盖默认的保存方式),但有一种方法非常简单。

注意你尝试时遇到的错误:

>>> 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 当你想要一个本质上是匿名的函数,或者在表达式中间定义的函数,但不要为了省三个字符而使用它。

58

你可以通过递归的方式遍历这个树结构,把每个 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}}}}

撰写回答