5

I try:

def test(w,sli):
    s = "'{0}'{1}".format(w,sli)
    exec(s)
    return s

print test("TEST12344","[:2]")

its return 'TEST12344'[:2]

How to return value from exec in function

TigerhawkT3
  • 48,464
  • 6
  • 60
  • 97
user7172
  • 874
  • 4
  • 16
  • 31

3 Answers3

18

Think of running the following code.

code = """
def func():
    print("std out")
    return "expr out"
func()
"""

On Python Console

If you run func() on the python console, the output would be something like:

>>> def func():
...     print("std out")
...     return "expr out"
...
>>> func()
std out
'expr out'

With exec

>>> exec(code)
std out
>>> print(exec(code))
std out
None

As you can see, the return is None.

With eval

>>> eval(code)

will produce Error.

So I Made My exec_with_return()

import ast
import copy
def convertExpr2Expression(Expr):
        Expr.lineno = 0
        Expr.col_offset = 0
        result = ast.Expression(Expr.value, lineno=0, col_offset = 0)

        return result
def exec_with_return(code):
    code_ast = ast.parse(code)

    init_ast = copy.deepcopy(code_ast)
    init_ast.body = code_ast.body[:-1]

    last_ast = copy.deepcopy(code_ast)
    last_ast.body = code_ast.body[-1:]

    exec(compile(init_ast, "<ast>", "exec"), globals())
    if type(last_ast.body[0]) == ast.Expr:
        return eval(compile(convertExpr2Expression(last_ast.body[0]), "<ast>", "eval"),globals())
    else:
        exec(compile(last_ast, "<ast>", "exec"),globals())

exec_with_return(code)
Allosteric
  • 871
  • 1
  • 9
  • 14
  • 1
    Wow, this is the only place I've seen this problem I've been having with None begin returned from exec command and what to do about it. Your code worked as a drop-in. Added your code, changed 1 line of my code, and it works! – Mike from PSG May 20 '19 at 12:04
  • I've been creating a mock backend server for testing the frontend, and I wanted to allow arbitrary Python expressions to be executed on the server. This has been extraordinarily handy — offering the familiar eval semantics of the IPython shell from my HTTP API. Thank you! – theY4Kman Feb 07 '20 at 00:36
  • The idea is great here and it was exactly what I needed to implement some behaviour similar to how the Python console works. However, I wonder if the implementation makes it look more complicated than it needs to be. Is there any technical reason for needing the two copies? Why not simply pop the node off code_ast.body if it is an Expr? Then, either way, code_ast can simply be exec'ed as is. Finally, if we did pop off the last node it can be converted to an Expression as above and eval'ed. – Bob Apr 08 '20 at 08:36
  • Clever, but it doesn't always do what you might expect. With the following input `'if True:\n "hello"\nelse:\n "world"'` the output is neither "hello", nor "world", but None. – Ian Goldby Feb 11 '22 at 09:51
5

exec() doesn't just evaluate expressions, it executes code. You would have to save a reference within the exec() call.

def test(w, sli):
    exec('s = "{}"{}'.format(w, sli))
    return s

If you just want to evaluate an expression, use eval(), and save a reference to the returned value:

def test(w,sli):
    s = "'{0}'{1}".format(w,sli)
    s = eval(s)
    return s

However, I would recommend avoiding exec() and eval() in any real code whenever possible. If you use it, make sure you have a very good reason to do so.

TigerhawkT3
  • 48,464
  • 6
  • 60
  • 97
  • 4
    This does not work in python 3, giving a `NameError` for `s` – Eric Jul 30 '18 at 16:59
  • @Eric As you can see by the OP's use of `print` as a statement rather than a function, Python 3 is not being used here. – TigerhawkT3 Aug 03 '18 at 20:40
  • You're right, but I and other users stumbling across this answer are increasingly more likely to be using python 3 - so I left a note to save them time trying it. – Eric Aug 06 '18 at 04:46
3

My findings in Python 3.8 in 2020

Eval Logic:

a = eval("1 + 99")
print(a) 
# Output: 100

Exec Logic:

exec("a = 1 + 99")
print(a) 
# Output: 100

So exec executes in the current scope.

WOWOW
  • 91
  • 8
nikhil swami
  • 2,360
  • 5
  • 15