2. Use explicit namespaces with exec and eval

Do not rely on mutating locals(). Pass explicit namespaces to exec() or eval() instead.

Relying on implicit local-scope mutation is hard to reason about and varies by execution context. Explicit mappings make the data flow visible and make dynamic execution code much easier to debug.

2.1. Don’t do this

1x = 1
2exec('x = 2', globals(), locals())
3print(x)

2.2. Do this

1scope = {'x': 1}
2exec('x = 2', {}, scope)
3print(scope['x'])