Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

exec() not working inside function python3.x

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.

like image 592
Ankit Malik Avatar asked Dec 12 '16 11:12

Ankit Malik


4 Answers

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.

like image 38
C-3PO Avatar answered Nov 06 '22 09:11

C-3PO


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

like image 110
DrM Avatar answered Nov 06 '22 08:11

DrM


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():\n\treturn('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

like image 4
Tushar Gautam Avatar answered Nov 06 '22 09:11

Tushar Gautam


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)
like image 1
Dimitris Fasarakis Hilliard Avatar answered Nov 06 '22 10:11

Dimitris Fasarakis Hilliard