如何将嵌套的defaultdict转为嵌套的dict?
根据这个回答,我创建了一个包含多个defaultdict
的defaultdict
。现在,我想把这个层层嵌套的字典对象转换回普通的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}}}}