Strange behaviour of the program after modifying the dictionary of local variables

Question:

I find some strange behavior in my program after modifying the dictionary of local variables, and it makes me confused. If we execute the code below, we will get the result as the image I attached. And now I know I shouldn’t modify the local variables using locals().

i=0
locals()[f'num_{i}'] = i
print(num_0)

def lc():
    i=1
    locals()[f'num_{i}'] = i
    import pdb; pdb.set_trace()
    print(num_1)


if __name__ == '__main__':
    lc()

My questions are:

  1. Why ‘num_0’ can be printed successfully while ‘num_1’ cant? (solved)

  2. Why ‘num_1’ can be printed in pdb but ‘print(num_1)’ cant be executed? (solved)

Result:

python test.py
-----
0
-> print(num_1)
(pdb) p num_1
1
(pdb) c
Traceback (most recent call last):
  File "test.py", line 13, in <module>
    lc()
  File "test.py", line 9, in lc
    print(num_1)
NameError: name 'num_1' is not defined

Actually, I have a few ideas but I am not sure. I checked the dictionary of global variables, and ‘num_0’ is in it. So I suppose the first question is because the interpreter doesn’t know that ‘num_1’ is in the dictionary of local variables, while ‘num_0’ is not only in the local dictionary but also in the global dictionary. As to the second question, I suppose pdb will know the modified local dictionary, so it can print ‘num_1’ successfully.

I hope someone can help to explain the two questions or give me some reference materials.

Asked By: x pie

||

Answers:

You are using locals() in a function scope

If you read the actual documentation, you’ll see the following:

Note: The contents of this dictionary should not be modified; changes may not affect the values of local and free variables used by
the interpreter.


The documentation makes no promises about what modifying the
dictionary will do.

At function scope, the dictionary locals() returns is not the primary
representation of the local scope. It’s a separate dictionary attached
to the stack frame. Calling locals() updates that dictionary with the
current values of local variables and returns the dictionary.

At function scope, changing the dictionary will usually not update
local variables, but in some special circumstances (mostly related to
debugging), Python will copy values from the dictionary back to local
variables.

At function scope, changing local variables will usually not update
the dictionary, unless you call locals() again or something accesses
the frame object’s f_locals attribute. Debuggers access f_locals to
read local variable values, so code that uses locals() will often
break in a debugger.

https://stackoverflow.com/a/71467339/6251742

why pdb can print ‘num_1’ successfully?

Because the debugger reads the locals, updating the dictionnary when you ask pdb the value. You can achieve the same behavior with breakpoint()

>>> def f():
...     x = 1
...     y = locals()
...     x = 2
...     breakpoint()  # update locals internal dict
...     x = 3  # locals internal dict doesn't change
...     print(y['x'])  # so it print values that were in locals at the last update
>>> f()
> <input>(6)f()
(Pdb) >? continue
2
Answered By: Dorian Turba
Categories: questions Tags: , ,
Answers are sorted by their score. The answer accepted by the question owner as the best is marked with
at the top-right corner.