[Tutor] eval use (directly by interpreter vs with in a script)

Steven D'Aprano steve at pearwood.info
Tue Nov 4 04:08:03 CET 2014


On Mon, Nov 03, 2014 at 09:33:18AM -0800, Albert-Jan Roskam wrote:

> >Real question is what you're trying to do.  eval() and exec() are to be 
> >avoided if possible, so the solutions are not necessarily the easiest.
> 
> I sometimes do something like
> ifelse = "'teddybear' if bmi > 30 else 'skinny'"
> weightcats = [eval(ifelse) for bmi in bmis]
> 
> Would this also be a *bad* use of eval? It can be avoided, but this is so concise.

Two lines, 92 characters. This is more concise:

weightcats = ['teddybear' if bmi > 30 else 'skinny' for bmi in bmis]

One line, 68 characters. And it will be faster too. On average, you 
should expect that:

eval(expression)

is about ten times slower than expression would be on its own.

In my opinion, a *minimum* requirement for eval() is that you don't know 
what the code being evaluated will be when you're writing it. If you can 
write a fixed string inside the eval, like your example above, or a 
simpler case here:

results = [eval("x + 2") for x in values]

then eval is unneccessary and should be avoided, just write the 
expression itself:

results = [x + 2 for x in values]


You *may* have a good reason for using eval if you don't know what the 
expression will be until runtime:

results = [eval("x %c 2" % random.choice("+-/*")) for x in values]


but even then there is often a better way to get the same result, e.g. 
using getattr(myvariable, name) instead of eval("myvariable.%s" % name). 
In the case of the random operator, I'd write something like this:

OPERATORS = {'+': operator.add, '-': operator.sub,
             '/': operator.truediv, '*': operator.mul}
results = [OPERATORS[random.choice("+-/*")](x, 2) for x in values]

which in this case is a little longer but safer and probably faster. 
It's also more easily extensible to a wider range of operators and even 
functions.



-- 
Steven


More information about the Tutor mailing list