Python Dynamic Code Execution Functions
What’s the difference between eval, exec, and compile? - Stack Overflow
Understanding the three core functions for dynamic code execution in Python:
eval() - Expression Evaluation
Purpose: Evaluates a single Python expression and returns the result
1
2
3
| result = eval("2 + 3 * 4") # Returns 14
x = 5
result = eval("x * 2") # Returns 10
|
Characteristics:
- Single Expression: Only works with expressions, not statements
- Return Value: Always returns a value
- Use Cases: Mathematical calculations, simple expressions
- Limitations: Cannot handle statements like assignments or loops
exec() - Statement Execution
Purpose: Executes Python statements (does not return a value)
1
2
3
4
5
| exec("x = 10; y = 20; print(x + y)") # Prints: 30
exec("""
for i in range(3):
print(f"Hello {i}")
""")
|
Characteristics:
- Multiple Statements: Can execute complex code blocks
- No Return Value: Returns None
- Use Cases: Dynamic code execution, configuration scripts
- Flexibility: Can handle any valid Python code
compile() - Code Object Creation
Purpose: Compiles source code into code objects for repeated execution
1
2
3
4
5
6
| # Compile once, execute multiple times
code = compile("x * 2", "<string>", "eval")
x = 5
result1 = eval(code) # Returns 10
x = 10
result2 = eval(code) # Returns 20
|
Modes:
- ’eval’: For expressions (use with eval())
- ’exec’: For statements (use with exec())
- ‘single’: For single interactive statements
Compilation Overhead:
eval() and exec() compile code every timecompile() allows pre-compilation for repeated use- Significant performance improvement for repeated execution
Example - Repeated Execution:
1
2
3
4
5
6
7
8
9
10
11
12
| import timeit
# Without compile() - slower
def slow_version():
for i in range(1000):
eval("2 + 3 * 4")
# With compile() - faster
code = compile("2 + 3 * 4", "<string>", "eval")
def fast_version():
for i in range(1000):
eval(code)
|
Security Considerations
Major Risks:
- Code Injection: User input can execute arbitrary code
- System Access: Malicious code can access file system, network
- Data Exposure: Can access and modify global variables
Safer Alternatives:
1
2
3
4
5
6
7
8
| # Restricted globals and locals
safe_globals = {"__builtins__": {}}
safe_locals = {"x": 10, "y": 20}
result = eval("x + y", safe_globals, safe_locals)
# Use ast.literal_eval for safe data parsing
import ast
data = ast.literal_eval("{'key': 'value'}") # Only literals
|
Best Practices
- Avoid When Possible: Use alternative approaches first
- Sanitize Input: Never execute untrusted user input
- Restrict Scope: Use limited globals and locals dictionaries
- Use ast.literal_eval: For parsing data structures safely
- Pre-compile: Use compile() for repeated execution
These functions provide powerful dynamic execution capabilities but require
careful consideration of security and performance implications.