What is eval in Python?
In Python, we have many in-built methods that are really essential towards making Python a convenient language for all and one such method is eval. The syntax of the eval function is given below:
eval(expression, globals, locals)
As we can see above, the eval function takes three parameters:
- expression – it takes a string that is parsed and evaluated as a Python expression
- globals (optional) – a dictionary to specify the available global methods and variables.
- locals (optional)- another dictionary to specify the available local methods and variables.
The use of globals and locals will be shown later in this article.
What does eval do in Python?
The eval function parses the expression argument and evaluates it as a python expression. In other words, we can say that this function parses the expression passed to it and runs python expression(code) within the program.
To evaluate a string-based expression, Python’s eval function runs the following steps:
- Parse expression
- Compile it to bytecode
- Evaluate it as a Python expression
- Return the result of the evaluation
This means that when we pass any python expression as a “string” to the eval function, it evaluates the expression and returns the result as an integer or float. Here are simple examples that will make things much clear to you.
Here is a simple way to convert your string into an integer, complex number or float using eval in Python:
num="23"
float_num="53.332"
complex_num="2+3j"
str1="Not number"
print(eval(num),type(eval(num)))
print(eval(float_num),type(eval(float_num)))
print(eval(complex_num),type(eval(complex_num)))
print(eval(str1),type(eval(str1)))
OUTPUT: 23 53.332 (2+3j) Traceback (most recent call last): File "main.py", line 8, in print(eval(str1),type(eval(str1))) File "", line 1 Not number ^ SyntaxError: unexpected EOF while parsing
As you can see, the eval function was able to identify the expression in the string and converted them to respective types. But when we passed just characters and alphabets, it returned an error. This should make clear what actually does eval do.
Here are some more examples in which we more a little more than type conversions, we actually see eval function evaluating the expressions inside a string.
We can also solve mathematical expressions using eval:
expr="(2+(3*2))/2"
print(eval(expr))
OUTPUT: 4.0
We can even use variables names inside the string and Python will also evaluate them as shown below
num=10
expr="(2+(3*2))/2 + num"
print(eval(expr))
OUTPUT: 14.0
We can also use in-built functions inside the strings as shown below:
print(eval("sum([8, 16, 34])"))
OUTPUT: 58
To get more understanding of eval function, let us see how it responds if I enclose the expression in two strings as shown below:
#string in another string
expr="'2+3'"
print(eval(expr))
print(eval(eval(expr)))
OUTPUT: 2+3 5
So the first eval function just returned the expression in the string, but when using eval inside another eval function, we got the answer to the expression.
How to use eval in python?
We have already seen how to use eval function in the above section, but here we are going to see how the other arguments to the eval function affect its working. So the eval in Python has two more arguments, viz-globals and locals.
Globals are the objects that are available in your current global scope or namespace. You can access them from anywhere in your code.
All the objects passed to globals in a dictionary will be available to eval() at execution time. Check out the following example, which shows how to use a custom dictionary to supply a global namespace to the eval function:
num1 = 100 # A global variable
print(eval("num1 + 100", {"num1": num1}))
num2 = 200 # Another global variable
print(eval("num1 + num2", {"num1": num1,"num2": num2}))
print(eval("num1 + num2", {"num1": num1}))
OUTPUT: 200 300 Traceback (most recent call last): File "main.py", line 5, in print(eval("num1 + num2", {"num1": num1})) File "", line 1, in NameError: name 'num2' is not defined
As you can see in the above example, first the eval is only able to access num1 and num2, but when I remove num2 from the globals dictionary, it throws an error as it could not recognise num2 now. But why didn’t such error occur in the above examples where I didn’t even pass values to the globals argument?
It turns out when you call eval function without supplying a globals argument, the function evaluates an expression using the dictionary returned by globals() functions as its global namespace. So, in the above example, we could freely access all the variables because they’re global variables included in your current global scope.
Now what happens if we pass an empty dictionary to globals, let us see:
a=2
print(eval("sum([2, 2, 2])", {}))
print(eval("sum([a, 2, 2])", {}))
OUTPUT: 6 Traceback (most recent call last): File "main.py", line 3, in print(eval("sum([a, 2, 2])", {})) File "", line 1, in NameError: name 'a' is not defined
So, the eval function could successfully recognise the function sum, but couldn’t recognise the object ‘a’ and thus returned an error.
When we supply a custom dictionary to the globals, it contains a value for the key “__builtins__”, but if it doesn’t then a reference to the dictionary of builtins will be automatically inserted under “__builtins__” before expression gets parsed. This ensures that eval() function will have full access to all of Python’s built-in names when evaluating the expression. This explains how the function sum is recognised by eval in the above example.
Now let us see what are locals and how do they extend the functionality of eval function. Unlike globals, local objects are declared inside a function and cannot be accessed outside that function. Similarly, the locals argument takes a dictionary in which we add some objects and the eval() function treat those objects as local objects. Take the example below:
print(eval("sum([a, 2, 2])",{}, {"a":2}))
print(a)
OUTPUT: 6 Traceback (most recent call last): File "main.py", line 2, in print(a) NameError: name 'a' is not defined
Note that to supply a dictionary to locals, you first need to supply a dictionary to globals. It’s not possible to use keyword arguments with eval()
This might seem confusing, but here are some examples in which I use both globals and locals arguments and you will see how they affect the results.
print(eval("abs(-1)"))
#By keeping __builtins__":None,eval will recognise no in-buiilt function
print(eval('abs(-1)',{"__builtins__":None}))
OUTPUT: 1 Traceback (most recent call last): File "main.py", line 1, in print(eval('abs(-1)',{"__builtins__":None})) File "", line 1, in TypeError: 'NoneType' object is not subscriptable
Now, we want that this function should work in eval function, thus we add it to the local dictionary. And now the eval function can recognise the abs function and not any other functions.
print(eval('abs(-1)',{"__builtins__":None},{"abs":abs}))
OUTPUT: 1
The main practical difference between globals and locals is that Python will automatically insert a “__builtins__” key into globals if that key doesn’t already exist. This happens whether or not you supply a custom dictionary to globals. On the other hand, if you supply a custom dictionary to locals, then that dictionary will remain unchanged during the execution of eval function.
Limitations of eval
The eval() in Python is quite useful but it also has important security implications. eval function is considered insecure because it allows you or other users to dynamically execute arbitrary Python code. So how does it affect us?
Suppose you are asking for user’s input in an application that runs on your server. Now if you are using eval function on input, then the user has access to the server itself. The user can pass some shady code like this:
__import__('subprocess').getoutput('rm –rf *')
The above code would delete all the files in the application’s current directory and that is surely going to affect us.
So it is always better to avoid eval function but if you have to use it anyway, we can restrict its functionality with the help of globals and locals argument. As we saw in the last section, we restricted the eval function in such a way that it could only use the abs function of python.
For example, say I have an application that can find a minimum among given numbers or sum of all given numbers. The most convenient way to use eval like this
print(eval(input()))
#input:sum([1,3,4])
#output:8
#input:min([1,3,4])
#output:1
But, this is a bad way of programming. We cannot control what user inputs so we can make use of globals and locals argument in such a way that no function other than sum() and min() is recognised by eval. This will surely do the same thing as the above code but it is much safer.
print(eval(input(),{"__builtins__":None},{"min":min,"sum":sum}))
This brings us to the end of this article where we learned about eval function in Python. To get a free Python course, click the banner below: