'Why does the dictionary seem to change between the last statement in the function and the first statement after the function call?

In this solution to convert a dot notation string to a nested dictionary and set a value, I coudn't understand why the desired result was achieved. Line 4 in particular didn't look right. So I tried

obj, path, value = {}, 'test.test1', 10
*path, last = path.split(".")
for bit in path:
    obj = obj.setdefault(bit, {})
obj[last] = value

print(obj)

Output

{'test1': 10}

This confirmed my suspicions as the expected result is {'test': {'test1': 10}}.

But when the code (added print statements to see what is happening) runs inside a function the dictionary changes to the desired result only outside the function call.

def setv(obj, path, value):
    *path, last = path.split(".")
    for bit in path:
        obj = obj.setdefault(bit, {})
    print(obj)
    obj[last] = value
    print(obj)

obj, path, value = {}, 'test.test1', 10
setv(obj, path, value)
print(obj)

Output

{}                      # inside the function call
{'test1': 10}           # inside the function call
{'test': {'test1': 10}} # after the function call

What subtle difference am I missing here that explains this behavior?



Solution 1:[1]

Inside setv, at the time it is printed, obj is a local variable, because there was an assignment obj = ....

The assignment shadows the global variable (or the argument, they are identical) obj.

You can verify it by printing id(obj) as well.

The fact that the global obj is a dictionary that contains a reference to the local obj makes this more confusing but doesn't actually matter. The same can be observed with simpler objects:

>>> def f(a):
...     print(a)
...     a = 7
...     print(a)
...

>>> a = 10
>>> f(a)
10
7
>>> print(a)
10

Sources

This article follows the attribution requirements of Stack Overflow and is licensed under CC BY-SA 3.0.

Source: Stack Overflow

Solution Source
Solution 1