简体   繁体   中英

Using a loop to .setdefault on Dict Creates Nested Dict

I'm trying to understand why

tree = {}

def add_to_tree(root, value_string):
    """Given a string of characters `value_string`, create or update a
    series of dictionaries where the value at each level is a dictionary of
    the characters that have been seen following the current character.
    """
    for character in value_string:
        root = root.setdefault(character, {})

add_to_tree(tree, 'abc')

creates {'a': {'b': {'c': {}}}}

while

root = {}

root.setdefault('a', {})
root.setdefault('b', {})
root.setdefault('c', {})

creates {'a': {}, 'b': {}, 'c': {}}

What is putting us into the assigned dict value on each iteration of the loop?

root.setdefault(character, {}) returns root[character] if character is a key in root or it returns the empty dict {} . It is the same as root.get(character, {}) except that it also assigns root[character] = {} if character is not already a key in root .


root = root.setdefault(character, {})

reassigns root to a new dict if character is not already a key in the original root .

In [4]: root = dict()

In [5]: newroot = root.setdefault('a', {})

In [6]: root
Out[6]: {'a': {}}

In [7]: newroot
Out[7]: {}

In contrast, using root.setdefault('a', {}) without reassigning its return value to root works:

tree = {}

def add_to_tree(root, value_string):
    """Given a string of characters `value_string`, create or update a
    series of dictionaries where the value at each level is a dictionary of
    the characters that have been seen following the current character.
    """
    for character in value_string:
        root.setdefault(character, {})

add_to_tree(tree, 'abc')
print(tree)
# {'a': {}, 'c': {}, 'b': {}}

For anyone else who is as slow as me. The answer to, "Why does the (above) function produce {'a': {'b': {'c': {}, 'd': {}}}} and not {'a': {}, 'b': {}, 'c': {}} ?" is:

Because we're looping within a function and reassigning the the result to root each time, it's kind of like in the TV infomercials where they keep saying, “but wait! there's more!”. So when .setdefault gets called on 'a', before that gets returned, it's result, {'a': {}} is held {inside the loop} while it's run on 'b', which yields {'b': {}} , within {'a': {}} and that is held to the side and {'a': {}} is run, then the whole thing is returned from the loop and applied to tree. Note that each time, what is actually returned by .setdefault IS the default, which in this case is {} . Here is a Python Visualizer illustration of the process.

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM