Python has a built-in function named exec()
which is present in the math library. It is used to dynamically execute the python generated object code or strings. Using exec()
function is not recommended because of its side effects. Consider exec() function as a container in your python, to which you provide some code, and it will execute the complete code inside it but will not return anything. Which means we cannot use it with return
or yield statements.
Syntax of exec()
Function:
exec(source [, globals [, locals]])
Here, the source can be a string(python statements), or a code object(for example an open file object in which python code is specified).
In the case of string which means there are one or more python statements, the string is first parsed and then executed.
Whereas, in case of code object it is directly executed(In case of an open file object, the file is parsed until the EOF and then executed).
Once again, we would like to specify that this function doesn't return anything even if the code block that you have provided as source returns anything. You can only print information on the console from the code that you execute inside the exec()
function. It returns None.
Quick Fact: exec was a statement in Python 2 whereas in Python 3 it was converted into a function.
Out of the three parameters passed to the exec()
method. The first parameter is compulsory, but the second and third parameters are optional.
The global parameter could be a dictionary(commonly used) and the local parameter is usually the mapping object.
Time for an Example!
Let's take a simple example to see the exec() function in action:
somecode = "print("Hello World!")"
exec(somecode)
Output:
Hello World!
NOTE: In order to use the exec()
method properly, one must be aware of the functions supported by it. This could be determined using dir()
function. All you have to do is execute the dir()
function using the exec()
function to print the name of functions supported:
import math
exec("print(dir())")
Output:
['In', 'Out', '__builtin__', '__builtins__', '__doc__', '__loader__', '__name__', '__package__', '__spec__', '_dh', '_i', '_i1', '_i2', '_i3', '_i4', '_i5', '_ih', '_ii', '_iii', '_oh', 'acos', 'acosh', 'asin', 'asinh', 'atan', 'atan2', 'atanh', 'ceil', 'isinf', 'isnan', 'ldexp', 'lgamma', 'log', 'log10', 'log1p', 'log2', 'math', 'modf', 'nan', 'pi', 'pow', 'quit', 'radians', 'remainder', 'sin', 'sinh', 'sqrt', 'tan', 'tanh', 'tau', 'trunc']
The output of the above code can be different for different python installations. So don't worry if you don't get the same output.
1. Using the exec()
function with just one parameter
Let's see a simple example where we will be passing a code block to the exec()
function for executing it. In the code below we have a for
loop printing some text in a loop.
# multiline code
code_to_execute = '''
for i in range(2):
print('Studytonight is fun')
'''
exec(code_to_execute)
Output:
Studytonight is fun
Studytonight is fun
In the code above, code_to_execute
is executed by passing it to the exec()
method. The code block being nothing else but a string is first parsed and then passed as a parameter to the exec()
function which gives the above output.
2. Understanding the globals
and locals
optional parameters
The globals and the locals optional parameters define the scope of execution of the object/string using exec()
function.
But what does that mean? What is the need of global parameter? How to use local parameter? Let's take some examples to understand this.
Sometimes, not all the methods and variables which can be used with exec()
function are needed. Hence, the global parameter sets the priority for the functions which could be used inside exec()
function.
When the local parameter hasn't been mentioned, the global parameter takes the place of the local parameter as well.
from math import *
exec("print(dir())", {})
Output:
['__builtins__']
An empty dictionary is passed as the globals parameter. This means no method from the math library will be available for execution (even though the entire library has been imported) because we have provided an empty globals parameter, hence the default which was being used is now not used as we have provided the value ourselves.
Going a little deeper, globals()
function by default represents the current global symbol table. This is a dictionary of the current module with its function name etc.
Let's take another example where we will be using the locals optional mapping object to provide some value to the code being executed inside the exec()
function.
import math
exec("print(a+b)", globals(), {'a': 42, 'b':8})
Output:
50
3. Restricting and Renaming functions allowed in exec()
function
As specified above, we can control the functions available to be executed inside the exec() function. So let's see more examples for it. In the code below, we have restricted the math module making only the square root and power functions available in exec()
function.
from math import *
exec('print(dir())', {'sqrt': sqrt, 'pow': pow})
Output:
['__builtins__', 'pow', 'sqrt']
The second parameter (globals) here specifies that apart from the __builtins__
object, the sqrt
and the pow
methods have to be made available for execution.
All exec()
function calls in the code above indicate that sqrt
and pow
functions from the math library are available to be executed on the object/strings passed to the exec()
method.
Also, when in the third statement we try to use the ceil()
function of the math module, we get an error.
Now your question could be, can we restrict the usage of the __builtins__
objects also by specifying it in the exec method?
The answer is Yes. It could be restricted by specifying the __builtins__
object as None
, below we have a code example for that.
exec(object/string, {'__builtins__': None})
We can even provide a different name for the functions allowed in the globals dictionary. Below is a code example for that.
from math import *
exec('print(dir())', {'square_root': sqrt, 'power_raised': pow})
exec('print(square_root(9))', {'square_root': sqrt, 'pow': pow})
exec('print(power_raised(9,2))', {'square_root': sqrt, 'power_raised': pow})
Output:
['__builtins__', 'power_raised', 'square_root']
3.0
81.0
In the code above, the name of the built-in functions from the math library has been modified and passed as parameters in the form of a dictionary. This way, the built-in function name is mapped to the modified function name.
Time for more examples!
Now that we know enough about how we can use the exec() function and what all we can do with various parameters provided to it. Let's see some code examples.
In the last example, you can add any python code to execute, try adding the following: [print(x**3) for x in range(6)]
And the code will get executed. This is something which should not be done in real-world applications.
You should never allow a user to execute their code like this in your application because the user can execute anything, and can seriously hamper your system. Therefore it is advised to use the exec() function wisely.
Conclusion
The exec()
function is a useful python function but has very use-case specific approach. People use it to create false compiler impression where they ask a user to enter python code to execute and they use the exec()
method to execute the code. But it is not a very good and safe approach. If you have ever used this function do share the use-case with us.
You may also like: