TomCho
TomCho

Reputation: 3517

Creating a dict with dictionary comprehension and eval() gives me NameError

I am trying to create a dictionary with dictionary comprehensions in the following way (which is part of a much larger code)

columns = ['zeta', 'Lm', 'u_mean']
print('zeta', eval('zeta'))
print(locals())
dic = {col: [eval(col)] for col in columns}

The first print prints exactly as expected (the value of the variable zeta) and the second print confirms that zeta is in the locals dictionary, but in the dictionary comprehension command python fails with this error

NameError: name 'zeta' is not defined

Unfortunately, when trying to reproduce to error in order to post here, I found out that I can't reproduce the error, because the following commands work in ipython:

zeta,Lm,u_mean=1,4,69
columns=['zeta', 'Lm', 'u_mean']
print('zeta',eval('zeta'))
print(locals())
dic={ col : [eval(col)] for col in columns }

It is only those commands inside my code that do not work. So, am I missing something? Is there some test I can do to see what's wrong?

Upvotes: 4

Views: 560

Answers (1)

Martijn Pieters
Martijn Pieters

Reputation: 1123270

A dictionary comprehension is executed in a new scope, a lot like a nested function call. You cannot expect to access the locals of the parent scope in a list comprehension.

I strongly recommend you do not use locals like this. Create a separate dictionary to act as a namespace and look up your columns in that:

namespace = {
    'zeta': value_for_zeta,
    # ... etc.
}

then use {col: [namespace[col]] for col in columns}.

Failing that, you can store the locals() dictionary in a new variable and reference that; either directly, or through passing it in as the namespace for eval():

namespace = locals()
dic = {col: [eval(col, namespace)] for col in columns}

or simply:

namespace = locals()
dic = {col: [namespace[col]] for col in columns}

This works now because namespace is a closure; a name taken from the parent scope.

Note that the same limits apply to generator expressions, set comprehensions, and in Python 3, list comprehensions. Python 2 list comprehensions were implemented before all the other types and followed a different implementation strategy that did not involve a new scope, but this approach did not allow for generator expressions to work and the new approach with a separate scope was generally found to work better.

Upvotes: 6

Related Questions