【问题标题】:How to convert defaultdict of defaultdicts [of defaultdicts] to dict of dicts [of dicts]?如何将 defaultdicts [of defaultdicts] 的 defaultdict 转换为 dicts [of dicts] 的 dict?
【发布时间】:2014-12-17 06:37:26
【问题描述】:

使用this answer,我创建了defaultdictdefaultdicts。现在,我想把那个嵌套很深的 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 也不够用,因为它不会在嵌套的 dict(s) 上递归。

【问题讨论】:

  • 你为什么需要来转换这些?
  • 如果你最后不想要defaultdict,你有没有考虑写一个包装器,在普通的dicts 上使用setdefault 来生成东西?在构建时让你的代码稍微复杂一点可能是一个更好的解决方案,而不是让它稍微简单一点,然后再增加一个额外的复杂性……
  • @abarnert 我唯一的答案是上面的基本回答....我对这个问题很好奇,认为可能有一个聪明的解决方案使用 reduce() 或 itertools 或其他东西.我知道我可以问一下酸洗的事……但我有点卡在这个问题上。我最近一直在研究 python 的这些部分,想看看我是否能学到一些新东西。 tl;博士因为我是个白痴
  • 我看到了这一点,但只是现在我看到了答案。这就是为什么我问这个问题的原因......我经常认为没有有效/正确的方法来解决特定问题,所以我需要回溯,直到当前形式的问题“消失”。然而,我也经常遇到我从未想过的答案,使用我根本不知道的语言功能或其他智能。我问我想有类似的东西——我只是还不知道。

标签: python python-2.7 dictionary collections


【解决方案1】:

您可以在树上递归,将每个 defaultdict 实例替换为由 dict 理解生成的 dict:

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

【讨论】:

  • 这是一个很好的解决方案,但由于递归限制,不适用于非常大的字典。
  • @TennesseeLeeuwenburg:只有当“非常大”是指“占用一半内存”或“深度接近 1000”时,这两者都不是常见的问题。无论如何,您可以通过将其从自上而下的递归更改为自下而上的递归来解决第一个问题,第二个是通过在显式堆栈上使用循环而不是递归来解决,但是任何一个都会使您的代码变得更加复杂一个你通常不会遇到的问题……
  • items替换iteritems为python3
  • 如果你有它们两者的嵌套组合,我的意思是 dict 到 dicts 或任何顺序的 defaultdicts,这种方法将不起作用。您可能需要将 if 条件更改为 if isinstance(d, defaultdict) or isinstance(d, dict)
  • @ShivamKThakkar:那就用if isinstance(d, dict):defaultdictdict 的子类,并且同样通过了该实例测试。而isinstance() 将一个元组作为第二个参数,如果您需要测试多种类型,其中这些类型不是另一个类型的子类,您可以使用isinstance(instance, (type1, type2, ...))
【解决方案2】:

你的actually trying to do 是腌制你的递归defaultdict。而且你不在乎你在 unpickling 时得到的是 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 会导致问题——你不能在运行时内省你的函数,你会在调试器中得到更糟糕的回溯,等等。使用@987654335 @ 当你想要一个固有的匿名函数,或者你可以在表达式中间定义的函数,但不要用它来节省输入的三个字符。

【讨论】:

  • 腌制的defaultdict 是否会在另一个未定义factory 的脚本中正常加载?
  • 我刚刚测试过(with open("d.pickle", "wb") as fh: dump(defdict, fh) 在一个脚本中,with open("d.pickle", "rb") as fh: defdict = load(fh) 在另一个脚本中),结果是AttributeError: Can't get attribute 'factory' on &lt;module '__main__'&gt;。像stackoverflow.com/a/26496899/1878788 那样转换为普通的普通字典有帮助。
猜你喜欢
  • 2019-08-28
  • 1970-01-01
  • 1970-01-01
  • 2013-05-25
  • 2017-05-17
  • 2021-06-02
  • 1970-01-01
  • 2021-07-25
  • 2013-12-24
相关资源
最近更新 更多