3

A little easy problem:

exec("a=3")
print(a)

# This will print 3

If I use this:

def func():
    exec("a=3")
    print(a)

func()

# NameError: name 'a' is not defined.

What happened?How could I use exec() to assign it a value in a function?

Edit:I found a question with the same trouble but still didn't solved.

why do you want to do that?

I know using exec() is bad and unsafe.But recently I try to solve a OP's problem.I met it.

MisterMiyagi
  • 44,374
  • 10
  • 104
  • 119
jizhihaoSAMA
  • 12,336
  • 9
  • 27
  • 49

2 Answers2

5

Python knows several kinds of scope: module global, function local, nonlocal closures, class body. Notably, scope resolution is defined statically at byte code compile time – most importantly, whether names refer to local/nonlocal or global scope cannot be changed.

Of these scopes, only global scope is guaranteed to behave similar to a dict, and as such writeable. The local/nonlocal scope is generally not writeable, and new variables cannot be added to it.

exec will write to the global scope if locals is not passed in; globals must then explicitly be set to its default of globals().

def func():
    exec("a='exec'", globals())  # access only global scope
    print(a)

a = 'global'
func()  # prints exec

However, once a name is local to a function, exec cannot modify it.

def func():
    a = 'local'  # assignment makes name local
    exec("a='exec global'", globals())
    exec("a='exec locals'", globals(), locals())
    print(a)

a = 'global'
func()  # prints local

While a dict-like representation of local/nonlocal scope exists, the interpreter is not required to honour changes to it.

locals()

Update and return a dictionary representing the current local symbol table. Free variables are returned by locals() when it is called in function blocks, but not in class blocks. Note that at the module level, locals() and globals() are the same dictionary.

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.

Even though exec does take locals as a dict, these are not treated like function locals/nonlocals. Attempts to modify the default locals (the result of locals()) are not defined.

exec()

... If globals and locals are given, they are used for the global and local variables, respectively. If provided, locals can be any mapping object. Remember that at module level, globals and locals are the same dictionary. If exec gets two separate objects as globals and locals, the code will be executed as if it were embedded in a class definition.

Note: The default locals act as described for function locals() below: modifications to the default locals dictionary should not be attempted. ...

Community
  • 1
  • 1
MisterMiyagi
  • 44,374
  • 10
  • 104
  • 119
  • 1
    I think you maybe misunderstand me.Maybe you can tell me what's the principle of `exec()`(like how does it work.).In the second example of my code,All of them have no connection with the `global` or `nonlocal` and so on(it is just local variable in the function.So `exec()` execute this python code.Why couldn't print it?).Whatever,thanks for your work. – jizhihaoSAMA Mar 30 '20 at 12:26
  • @jizhihaoSAMA I've added two examples of how your code makes variables global or local, and how this affects the behaviour of ``exec``. – MisterMiyagi Mar 30 '20 at 12:33
2

execute help(exec) in your python3 REPL, you will get the following docuement:

Help on built-in function exec in module builtins:

exec(source, globals=None, locals=None, /)
    Execute the given source in the context of globals and locals.

    The source may be a string representing one or more Python statements
    or a code object as returned by compile().
    The globals must be a dictionary and locals can be any mapping,
    defaulting to the current globals and locals.
    If only globals is given, locals defaults to it.

so there are at least 2 options to provide the value of argument 'a':

  1. assign a value to the variable 'a' in the module's global scope:
a = 1

def func():
    exec("global a;a=3")
    print(a)
  1. pass a customized global or local context to exec:
def func():
    my_context = {'a': 1}
    exec("a=3", None, my_context)
    print(my_context['a'])

NOTE: DONT USE eval or exec IN YOUR SERIOUS CODE UNLESS YOU KNOW WHAT YOU ARE DOING.


EDIT NOTE

the following solution(2th solution mentioned in the comments) wont work:

def func():
   a = 1
   exec("a=3")
   print(a) # still get 1 here
jizhilong
  • 124
  • 5