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

Albert-Jan Roskam fomcl at yahoo.com
Tue Nov 4 17:11:13 CET 2014


----- Original Message -----
> From: Steven D'Aprano <steve at pearwood.info>
> To: tutor at python.org
> Cc: 
> Sent: Tuesday, November 4, 2014 4:08 AM
> Subject: Re: [Tutor] eval use (directly by interpreter vs with in a script)
> 
> 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]

Aaah *slap against forehead*! I did not know this is legal. MUCH nicer indeed


> 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.
 
Hmm, I get 1900 occurrences of eval() (and 700 of frozenset, just curious) in Python. That's MUCH, I must be something wrong, but I am rushing now!

import os
import sys
import collections
os.chdir(os.path.dirname(sys.executable))
cmds = ["eval(", "frozenset"]
counter = collections.Counter()
for root, dirs, files in os.walk(".", topdown=False):
    for name in files:
        if name.endswith(".py"):
            contents = open(os.path.join(root, name))
            for line in contents:
                if not line.startswith("#"):
                    for cmd in cmds:
                        if cmd in line:
                            counter[cmd] += 1
print counter


More information about the Tutor mailing list