Nested defaultdict of defaultdict

前端 未结 8 2332
谎友^
谎友^ 2020-11-22 12:19

Is there a way to make a defaultdict also be the default for the defaultdict? (i.e. infinite-level recursive defaultdict?)

I want to be able to do:

x         


        
相关标签:
8条回答
  • 2020-11-22 12:32

    I based this of Andrew's answer here. If you are looking to load data from a json or an existing dict into the nester defaultdict see this example:

    def nested_defaultdict(existing=None, **kwargs):
        if existing is None:
            existing = {}
        if not isinstance(existing, dict):
            return existing
        existing = {key: nested_defaultdict(val) for key, val in existing.items()}
        return defaultdict(nested_defaultdict, existing, **kwargs)
    

    https://gist.github.com/nucklehead/2d29628bb49115f3c30e78c071207775

    0 讨论(0)
  • 2020-11-22 12:37

    Similar to BrenBarn's solution, but doesn't contain the name of the variable tree twice, so it works even after changes to the variable dictionary:

    tree = (lambda f: f(f))(lambda a: (lambda: defaultdict(a(a))))
    

    Then you can create each new x with x = tree().


    For the def version, we can use function closure scope to protect the data structure from the flaw where existing instances stop working if the tree name is rebound. It looks like this:

    from collections import defaultdict
    
    def tree():
        def the_tree():
            return defaultdict(the_tree)
        return the_tree()
    
    0 讨论(0)
  • 2020-11-22 12:41

    The other answers here tell you how to create a defaultdict which contains "infinitely many" defaultdict, but they fail to address what I think may have been your initial need which was to simply have a two-depth defaultdict.

    You may have been looking for:

    defaultdict(lambda: defaultdict(dict))
    

    The reasons why you might prefer this construct are:

    • It is more explicit than the recursive solution, and therefore likely more understandable to the reader.
    • This enables the "leaf" of the defaultdict to be something other than a dictionary, e.g.,: defaultdict(lambda: defaultdict(list)) or defaultdict(lambda: defaultdict(set))
    0 讨论(0)
  • 2020-11-22 12:41

    There is a nifty trick for doing that:

    tree = lambda: defaultdict(tree)
    

    Then you can create your x with x = tree().

    0 讨论(0)
  • 2020-11-22 12:44

    For an arbitrary number of levels:

    def rec_dd():
        return defaultdict(rec_dd)
    
    >>> x = rec_dd()
    >>> x['a']['b']['c']['d']
    defaultdict(<function rec_dd at 0x7f0dcef81500>, {})
    >>> print json.dumps(x)
    {"a": {"b": {"c": {"d": {}}}}}
    

    Of course you could also do this with a lambda, but I find lambdas to be less readable. In any case it would look like this:

    rec_dd = lambda: defaultdict(rec_dd)
    
    0 讨论(0)
  • 2020-11-22 12:49

    @nucklehead's response can be extended to handle arrays in JSON as well:

    def nested_dict(existing=None, **kwargs):
        if existing is None:
            existing = defaultdict()
        if isinstance(existing, list):
            existing = [nested_dict(val) for val in existing]
        if not isinstance(existing, dict):
            return existing
        existing = {key: nested_dict(val) for key, val in existing.items()}
        return defaultdict(nested_dict, existing, **kwargs)
    
    0 讨论(0)
提交回复
热议问题