Usually I don’t use exec because I’m afraid that I use it wrong and get some security vulnerabilities, but whenever I hade to and needed to get the value (I did had to do this in a couple of my projects), I usually pass a dict or an object and save the value to some attribute / key in them. A list can also be used.
Here’s the implementation:
code = """
test = "hello, test!"
result["test"] = test
"""
exec_results = {}
exec(code, { "result": exec_results })
print(exec_results["test"]) # prints "hello, test!"
And the code you posted should work, but the “execFunc” type might not be resolved and maybe give you warnings in IDEs.
If you really must, then use eval instead. If you’re using exec, you’re doing it wrong.
Preferably never use either one, of course. Thus avoiding two whole categories (*) of huge code stenches, never mind code smells.
(*) the categories of Arbitrary Code Execution As A Feature, and Obfuscating Code in String Literals (so it avoids standard checks from all the normal tools and resembles malware, at least if you tell them to ignore the actual eval and exec calls, without which I hope any code quality tool would raise hell).
eval(<expresson>) returns a value because expressions return a value and an eval call is an expression. exec(<statement(s)>) does not return a value because executing statements does not return a value. By default, an exec call at top level has the same effect as if you had replaced the call with the code. (Inside a class is similar buy I am not sure of the details.)
When you exec a function statement, it creates a function object and binds it appropriately. To execute the function and access its return value, you have to call it and do something with its return, as normal, and as you did. This is not a ‘workaround’.
Now been trying to tidy-up this code so that the exec() can only see the local vars which are explicitly specified while still allowing it to return a value:
code = """
print("testA: ", test)
test = "test2"
print("testB: ", test)
return test
"""
test = "test1"
localDict = {"test": test}
exec("def execFunc(test):" + code.replace("\n", "\n\t"), {}, localDict)
print("retVal: ", localDict["execFunc"](test))
print(localDict["test"]) # <- displays 'test1' but shouldn't that have been changed in the exec() to 'test2'
However the ‘test’ variable doesn’t seem to get changed to “test2”, see the last line
It can’t - you’re right. My mistake. Can you follow the context from exec into a function, with an expression containing a function call in eval? I suppose you might as well just call the function using normal Python code.
However the ‘test’ variable doesn’t seem to get changed to “test2”, see the last line
Python won’t allow reassignment of names, outside of their code’s scope, unless that name is declared global test or nonlocal test.
Given eval(s), Python expects s to contain one legal expression. ‘code line’ is not a Python grammar unit. As usual, s can contain '\n' character pairs and actual newline characters as long as either is escaped with a preceeding \ or the entire expression surrounded by parantheses, so that s is seen as one logical line and compiled as one expression.
exec(expression-statement) executes as usual by evaluating the expression and ignoring the result. It does not do the interactive mode trick of binding the result to _.
Again, eval evaluates an expression and returns its value. You passed a function definition statement. Please learn the difference. Exec does not return a value because execution of Python statements does not return a python object. Note that statements that do create a python object, namely import, def, and class, bind the object to a name required as part of the statement after attaching the name to the object as attribute __name__.
Having eval(statement) return None would generally be less useful than raising an exception showing where non-expression code starts. I call this fortunate;-). If you want to suppress the exception, wrap the call in a try statement.
There are languages which only have expressions. By intentional design, Python is not one of them because GvR’s research indicated that expression/statement separation made learning easier for beginners. Expression languages only need eval. Without statements, exec is not needed.
If f() returns the string ‘3 + 5’, then v = f() results in v == ‘3 + 5’ whereas v = eval(f()) results in v == 8, which is quite different from the string.
Where does this string come from? Do you get to decide whether or not the result is signaled through a return statement? What exactly is your use case?
I describe it as a ‘workaround’ because in most languages executing a block of code can ‘return’ a value, even if running code in that way is not preferred it can normaly be done anyway. It just seems a bit odd to me that Python doesn’t allow a return statement in a code block to return a value.
In languages that differentiate between “a block of code” and “an expression”, usually a block of code does not return a value. A function can return a value.
I kinow it might not be the Pythonic way but surely it would be quite simple to allow code in an exec() block to ‘return’ a value, at the moment if always returns None. This seems to be an example of Python being deliberately restricted to try and do things the way the Python developers want it to be used rather than just making reccomendations and allowing everyone to behave like adults and do things in a non-pythonic way when the situation demands it.
Python’s exec() is a function, which at the moment always returns ‘None’. Surely it wouldn’t be difficult to return a value from a ‘return’ statement if there is one, otherwise return ‘None’ as it does at the moment.
Perhaps I should have said, in most languages that have a ‘return’ statement a block of code can return a value. Although Python has a ‘return’ statement is always returns ‘None’.