0

In Python 2.7 I used to be able to use the following code (I'm using the emcee package):

def main():
    from emcee import moves
    emcee_moves = ['KDEMove(),0.5', 'DEMove(),0.5']
    mv = [(eval("moves." + _)) for _ in emcee_moves]

if __name__ == '__main__':
    main()

I use this because I feed the "moves" through an input parameters file (this is a little portion of a much larger code), which means they are read as strings. In Python 3.x this now throws:

*** NameError: name 'moves' is not defined

This is apparently related to this wont fix bug as mentioned in this old question: Eval scope in Python 2 vs. 3.

I've also read that using eval() is generally discouraged. My question is then: how do I replicate the above code which used to work in Python 2.7?


Edit

This is the actual code that fails. It need to be inside a function, otherwise it won't fail.

Gabriel
  • 40,504
  • 73
  • 230
  • 404
  • I have Python 3.8.0 and the code works as expected - without any errors. – Smuuf Feb 26 '20 at 20:10
  • This is very strange. The code snippet works in my case too when I run it by itself (e.g., in a terminal session), but it fails when placed verbatim inside my larger code... – Gabriel Feb 26 '20 at 20:19
  • Seen your edit and then I tried to put the import in the global scope, above the function. **It worked.** _(And it didn't work when the import was done inside the function.)_. Even so, I would recommend skipping the whole `eval()` monstrosity and using `getattr()` instead. – Smuuf Feb 26 '20 at 20:24
  • The import **needs** to be inside the function as it should not be imported if the package is not there. As I said, this is part of a much larger code. – Gabriel Feb 26 '20 at 20:25

3 Answers3

2

As Pete said, your example works for python 3.6.8. However, another way of doing what you want is:

from emcee import moves
emcee_moves = [('KDEMove', 0.5), ('DEMove', 0.5)]
mv = [(getattr(moves, method)(), val) for method, val in emcee_moves]

getattr is generally safer than eval.

Cal
  • 631
  • 3
  • 11
  • 1
    Although this requires to modify the `emcee_moves` variable, it works as expected and I can get around that. Thank you Cal! – Gabriel Feb 26 '20 at 20:33
0

You can always replace the list comprehension with a regular loop. Less fancy, but gives the same functionality.

urim
  • 591
  • 4
  • 13
0

Although I have Python 3.8.0 and the code works as expected - without any errors., as my comment says, it's still possible to do these without any eval().

You want to dynamically get a class from a module. You can use getattr() for this.

from emcee import moves
emcee_moves = ['KDEMove', 'DEMove']
mv = [getattr(moves, _)() for _ in emcee_moves]

Output:

In [22]: mv = [getattr(moves, _)() for _ in emcee_moves]

In [23]: mv
Out[23]:
[<emcee.moves.kde.KDEMove at 0x7f2bf8b98a90>,
 <emcee.moves.de.DEMove at 0x7f2bf90b08e0>]

I'm not exactly sure why those ,0.5 suffixes were there (__init__ arguments, maybe?), but the "gist of it" is written above.

Smuuf
  • 6,339
  • 3
  • 23
  • 35
  • This fails with: `AttributeError: module 'emcee.moves' has no attribute 'KDEMove(),0.5'`. The `,0.5` indicates how those moves will be distributed (in this case, the moves will be used half the time each) so they are necessary. – Gabriel Feb 26 '20 at 20:24
  • So where does the actual value `0.5` go then? – Smuuf Feb 26 '20 at 20:25
  • See [Cal's answer](https://stackoverflow.com/a/60421903/1391441) – Gabriel Feb 26 '20 at 20:32