exec() not working inside function python3.x

Question:

I am trying to run this code but it seems that the exec() is not executing the string inside the function:

def abc(xyz):
    for i in fn_lst:
        s = 'temp=' + i + '(xyz)'
        exec(s)
        print (temp)

abc('avdfbafadnf')

The error I am receiving:

NameError                                 Traceback (most recent call last)
<ipython-input-23-099995c31c78> in <module>()
----> 1 abc('avdfbafadnf')

<ipython-input-21-80dc547cb34f> in abc(xyz)
      4         s = 'temp=' + i + '(word)'
      5         exec(s)
----> 6         print (temp)

NameError: name 'temp' is not defined

fn_lst is a list of function names i.e: ['has_at', 'has_num' ...]

Please let me know an alternative to exec() if possible in such a scenario.

Asked By: Ankit Malik

||

Answers:

Instead of using exec with function names, just keep the function objects in the list:

fn_lst = [has_at, has_num, ...]

and perform the call directly:

def abc(xyz):
    for i in fn_lst:
        temp= i(xyz)
        print(temp)

First we show how to make a variable set by the string passed to exec(), available outside of the call to exec(). And then we show some examples of how to make a variable available outside of a call to a function that calls exec().

The central concepts include that exec() takes as arguments, the string to be executed and two dictionaries to serve as global and local scope.

For example, we can pass the actual global and local scope, like this:

exec( 'a = 3', globals(), locals() )

print( a )

This will print the following result:

3

However, there is considerable flexibility in what dictionaries we choose to pass to exec(), and this provides a few ways to set a variable in the local scope from a function that calls exec().

For example, we can pass the current local scope to a function and then use that as the local dictionary for exec(), like this:

def demofunction( adict ):

    exec( 'a=1.', globals(), adict )

print( 'before calling the function' )
try:
    print( a )
except Exception as e:
    print( e )
    
demofunction( locals() )

print( 'after calling the function' )
print( 'a =', a )

This prints:

before calling the function
name 'a' is not defined

after calling the function
a = 1.0

Since the calling scope is global to the scope inside the function, another simple way to set a local variable from inside a function, is to just use globals() as the second argument for exec().

def demofunction( adict ):

    exec( 'a=1.', None, globals() )

print( 'before calling the function' )
try:
    print( a )
except Exception as e:
    print( e )
    
demofunction( locals() )

print( 'after calling the function' )
print( 'a =', a )

And this again, prints:

before calling the function
name 'a' is not defined

after calling the function
a = 1.0

So, we see that exec() in fact, can create variables in our local scope from inside a function.

Also, you are not limited to globals() and locals(). You can pass it any valid dictionary.

def demofunction( adict ):

    exec( 'a=1.', None, adict )


somedict = { 'b': 1 }
print( somedict )
    
demofunction( somedict )

print( somedict )

Now the output is:

{'b': 1}
{'b': 1, 'a': 1.0}

Note: In the first examples it would have been sufficient to use the local argument alone, i.e. omitting globals(). Both were included here to illustrate the more general case. You can read about "Scope" in Python, in the Python Textbook – Scope

Answered By: DrM

I would like to mention that many "standard" answers, previously suggested in this topic, do not work inside a function. For example, consider the following code snippet:

def test():
    exec( 'a = 3', globals(), locals() )
    print(a)
test()

Everything seems fine. However, this code gives an error in Python 3:

NameError: name 'a' is not defined

I tried some methods using the compile function suggested in other forums, but they still do not work for me (at least with the options I have seen mentioned).

According to my research, this the closest code that I have seen working:

def test():
    lcls = locals()
    exec( 'a = 3', globals(), lcls )
    a = lcls["a"]
    print(f'a is {a}')
test()

It successfully prints:

a is 3

I think this is an important topic overall. Sometimes when you work with symbolic algebra libraries, like Sympy, defining variables though the exec function can be very convenient for Scientific Computing.

I hope somebody knows a good answer to the problem.


EDIT:

Nowadays, I rarely use exec anymore. I have realized that a better/shorter solution to the OP question is to simply define local variables using the eval function. The code of the OP could have been written as:

def abc(xyz):
    for i in fn_lst:
        temp = eval(i + '(xyz)')
        print (temp)
abc('avdfbafadnf')
# problem solved :)

Since the variable name temp was already hard-coded into the function, using eval doesn’t change the generality of the solution. If the name of the variable isn’t known beforehand, eval can also be used as follows:

def advanced_eval(expr):
    var_names  = expr.split('=')[0].replace(' ','')
    rhs_values = eval('='.join(expr.split('=')[1:]))
    return var_names, rhs_values

If you define name,value = advanced_eval('a=3+3'), the code will effectively output that name = 'a' and value = 6.

Answered By: C-3PO

After spending so much time doing hit and trial on this problem, I can say that using exec like this was working without any problem inside function, otherwise it threw error.
I have tested this for both functions and variables.

def main():
  x = "def y():ntreturn('This will work')"
  #pass only globals() not locals()
  exec(x,globals())
  print(y())
  
main()

def test():
    #pass only globals() not locals()
    exec( 'a = 3', globals())
    print(a)
test()

Here is a screenshot of this working on W3School’s online interpreter (you can copy/paste and test it here yourself)
enter image description here

Answered By: Tushar Gautam

I search this problem because I tried to import variables of a configuration file into a function, but it failed.

After some trial, I got following conclusions:

  1. globals()[‘a’]=1 can change global variables.
  2. locals()[‘a’]=1 cannot change local variables in current scope, unless current scope is "main", where id(locals())==id(globals()).

This is why exec() for locals() works in "main" scope, but doesn’t work inside a function.

I think this is a trap of python’s scope mechanism.

Answered By: AndyHuang
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.