Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Python: How can I run eval() in the local scope of a function

Tags:

python

scope

eval

I try to use eval() in a local scope of a function. However it always evaluate in the global scope.

Self contained examples:

1- This code works:

var1 = 1
var2 = 2
var3 = 3    
myDict = dict((name, eval(name)) for name in ["var1",
                                              "var2",
                                              "var3"])
print(myDict["var1"])

2- Throws NameError for lvar1

def test1():
   lvar1 = 1
   lvar2 = 2
   lvar3 = 3
   myDict = dict((name, eval(name)) for name in ["lvar1",
                                                 "lvar2",
                                                 "lvar3"])
   print(myDict["lvar1"])

3- Same outcome as 2.

def test2():
    lvar1 = 1
    lvar2 = 2
    lvar3 = 3
    myDict = dict((name, eval(name), locals()) for name in ["lvar1",
                                                            "lvar2",
                                                            "lvar3"])
    print(myDict["lvar1"])
like image 507
atevm Avatar asked Apr 14 '16 07:04

atevm


People also ask

When should eval () be used?

Eval function is mostly used in situations or applications which need to evaluate mathematical expressions. Also if the user wants to evaluate the string into code then can use eval function, because eval function evaluates the string expression and returns the integer as a result.

What can I use instead of eval in Python?

literal_eval may be a safer alternative. literal_eval() would only evaluate literals, not algebraic expressions.

What is eval () function?

The Eval function evaluates the string expression and returns its value. For example, Eval("1 + 1") returns 2. If you pass to the Eval function a string that contains the name of a function, the Eval function returns the return value of the function.


2 Answers

Save the result of locals() (or vars()) call to return the function's local scope. Otherwise, locals() inside the generator expression will return the gen-expr's local scope.

def test3():
    lvar1 = 1
    lvar2 = 2
    lvar3 = 3
    scope = locals()
    myDict = dict((name, eval(name, scope)) for name in [
                  "lvar1", "lvar2", "lvar3"])
    print(myDict["lvar1"])

BTW, you don't need an explicit comprehension to build that dict:

# copy() avoids quirky, unexpected updates if something else (like a debugger)
# accesses locals() or f_locals
myDict = locals().copy()  # or vars().copy()
like image 157
falsetru Avatar answered Sep 28 '22 07:09

falsetru


First of all it's important to read this:

The expression argument is parsed and evaluated as a Python expression (technically speaking, a condition list) using the globals and locals dictionaries as global and local namespace. If the globals dictionary is present and lacks ‘__builtins__’, the current globals are copied into globals before expression is parsed. This means that expression normally has full access to the standard __builtin__ module and restricted environments are propagated. If the locals dictionary is omitted it defaults to the globals dictionary. If both dictionaries are omitted, the expression is executed in the environment where eval() is called. The return value is the result of the evaluated expression`.

To start with it is important to note that a generator expression has its own scope(true for a dict-comprehension as well), hence it has its own locals() dictionary.

  1. This worked because in global scope both globals() and locals() dict points to the same dictionary hence the dict constructor can access those variables.

  2. Here we are again calling eval() with no globals() and locals() dict hence it ends up using the global scope and its own local scope(which is empty) and there are no such variable available in any of these scopes.

  3. Remember generators have their own scope so calling locals() here barely makes any difference, it's an empty dict.

Solution:

def test1():
   lvar1 = 1
   lvar2 = 2
   lvar3 = 3
   test1_locals = locals()
   myDict = dict((name, eval(name, test1_locals)) for name in ["lvar1",
                                                 "lvar2",
                                                 "lvar3"])
   print myDict
   print(myDict["lvar1"])

This worked because we captured test1's locals() in a variable and then used that dictionary inside of the dictionary comprehension, so it now has access to those variables.

like image 36
Ashwini Chaudhary Avatar answered Sep 28 '22 09:09

Ashwini Chaudhary