Search code examples
pythonevalnameerror

Python: Eval giving NameError in function


I want to use eval to do operations to see if a and b equals c with some operator.

My code:

def Expression(a, b, c):
    operators = ["+", "-", "*", "/"]
    return len([i for i in operators if eval("a () b".replace("()", i)) == c]) >= 1

Expression(1, 2, 3)

For some reason, this results in a NameError. Error log:

return len([i for i in operators if eval("a () b".replace("()", i)) == c]) >= 1
  File "<string>", line 1, in <module>
NameError: name 'a' is not defined

Since the function has a as a parameter I don't believe a should be undefined. What is the problem here?


Solution

  • The problem is in that case eval try to find a and b in global scope, not function scop(it means that a and b is just valid in the function block). so you can pass the function current scope using locals() to eval like this:

    def Expression(a, b, c):
        operators = ["+", "-", "*", "/"]
        scope = locals()
        return len([i for i in operators if eval("a () b".replace("()", i), scope) == c]) >= 1
    

    Then your code will work.

    for better understanding try to define a and b in the global scope, then you can see it works, like this:

    a=1
    b=2
    def Expression(c):
        operators = ["+", "-", "*", "/"]
        return len([i for i in operators if eval("a () b".replace("()", i)) == c]) >= 1
    Expression(3)
    

    Also, you can pass a custom scope to eval by creating a dictionary and pass it to eval:

    scope = {'a':a, 'b':b}
    

    So, it was your code problem. but for the better attitude you can use what @Rakesh said before, Using formatted strings, it gets the current a and b and pass it to eval as what they have inside, like this:

    eval(f"{a}{i}{b}") # <Python3.6 
    eval("{}{}{}".format(a,i,b)) 
    

    Also you can use any() instead of len() >= 1