5

I generate a conditional statement using python's (2.7) eval() function like so:

my_list = ['2 > 1','3 > 2','4 > 3']

if eval('(' + ') or ('.join(my_list) + ')'):
    print 'yes'
else:
    print 'no'

In my case, the list is generated by code, my_list comes from a parameter file, and the list is joined with 'or' statements in the conditional expression. The code above prints 'yes'.

It works fine for small lists, but at a certain number of characters in the eval() statement and I get a string error.

Some searching finds these threads that point to a bug:

But their max eval() size is much larger than what I found. In my case, I find between 1744 and 1803 characters does the issue begin. I tried this code and it does crash between the two statements

>>> eval("1.0*"*10000+"1.0")
1.0
>>> eval("1.0*"*100000+"1.0")
# segfault here

So, that brings me back to think that it is not eval(), but actually some max on the if statement.

What's another way to conditionally apply the rules in the list that doesn't involve long strings and the eval() function?

Interestingly, I made my_list much bigger:

my_list = ['2 > 1']*1000000

and the code works fine...

Community
  • 1
  • 1
philshem
  • 24,761
  • 8
  • 61
  • 127
  • If the list of conditions is generated by code, why are they not evaluated immediately upon generation? Why the conversion to string at all? – Oliver W. Apr 17 '15 at 13:33
  • @OliverW. The conditions come from a config file. Because they are 'or' statements I don't see exactly how to evaluate one at a time. I guess I could have a boolean flag that starts as False and then is set to True if one of the 'if' statements evaluates as True. – philshem Apr 17 '15 at 13:35

1 Answers1

9

Perhaps I'm missing something but it would seem that:

any(map(eval, my_list))

Does exactly what you'd like.

from itertools import imap

any(imap(eval, my_list)) # Python 2.

This has the nice effect of not evaluating the rest of the list if the first element evals to True (also known as "short-circuit"). This may or may not be what you are after.

Example:

> any(map(eval, ['2 > 1','3 > 2','4 > 3']))
True
ereOn
  • 53,676
  • 39
  • 161
  • 238
  • 2
    Note: `map` is only lazy in Python 3. Under 2.7, [`itertools.imap`](http://docs.python.org/2/library/itertools.html#itertools.imap) can be used instead to get the same effect. (Just saying because OP is using Python 2.7.) – Carsten Apr 17 '15 at 13:39
  • This does exactly what I want, including the 'short-circuit'. There is a lot of hate for eval() but I don't see another way. – philshem Apr 17 '15 at 13:41
  • 1
    @philshem: There is hate for `eval`, for good reason but it also exists for a good reason. If your goal is to "evaluate some Python code" I'd say it's the most straightforward tool for the job. Just consider the security implications and whether this can be a problem to you. – ereOn Apr 17 '15 at 13:42
  • @Carsten what does 'lazy' in Python 3 mean? – philshem Apr 17 '15 at 13:47
  • 1
    @philshem `map` in python2 will immediately execute the map function on every element. `map` in python3 and `imap` in python2 will create an iterable where the map function is only evaluated when an element is consumed. So in the non-lazy case the eval is done on all elements, in the lazy-case only on the first element that evals to true and the elements before that. – KillianDS Apr 17 '15 at 13:48