views:

195

answers:

6

Hi! Is there a way (using eval or whatever) to evaluate eagerly boolean expressions in python?

Let's see this:

>>> x = 3
>>> 5 < x < y
False

Yikes! That's very nice, because this will be false regardless of y's value. The thing is, y can be even undefined, and I'd like to get that exception. How can I get python to evaluate all expressions even if it knows the result beforehand?

Hope I made myself clear! Thanks,
Manuel

Edit: Please bear in mind that the expression must not be modified, just the evaluation technique.

+4  A: 
all([5 < x, x < y])
Ignacio Vazquez-Abrams
Nope, that short-circuits too.
Alex Martelli
@Alex: Even when creating the list?
Ignacio Vazquez-Abrams
Alex Martelli
Creation of the list forces every single element to be evaluated. The only step that will be skipped is that of calling `__nonzero__()` or `__bool__()` on the results now stored in each element. This has no side effects with regards to `bool`, so we can ignore this minor difference for relational operators.
Ignacio Vazquez-Abrams
I couldn't really understand that explanation. Do you mean, for example, if x.__lt__ raises an exception?
Jimmy
@Jimmy What happens is, first a list is created. This list contains `5 < x` as the first element; `x < y` as the second. So it's a list of bools: if x or y is undefined, an exception will be raised here. Then all() walks through this list one item at a time until it has either exhausted the list (it returns True), or it encounters a false value (it returns False). It short circuits in that it doesn't consume all the iterable, which may skip side effects (like exceptions) if they occur during iteration or when checking whether an element is false. Lists and bools don't have such side effects.
Devin Jeanpierre
+3  A: 
(5 < x) & (x < y)

By using the bit-and operator, &, you get no short-circuiting behavior (as you get with and, or, chaining, all/any). Short-circuiting is normally deemed desirable (fast &c) but it's not hard to do without it if you really want;-).

Alex Martelli
Mike Graham
This is horrible.
Glenn Maynard
But, I suppose any answer to this question is going to be horrible.
Glenn Maynard
No, some answers are more horrible than others. This is very subtle, and the intent isn't clear on inspection (it just looks like a misuse of bitwise operators), which makes it far more horrible than, say, Mike Graham's answer, or in particular too much php's answer (clever [in a good way], if not general-case!)
Devin Jeanpierre
+3  A: 
>>> x = 3
>>> y > x > 5
Traceback (most recent call last):
  File "", line 1, in 
NameError: name 'y' is not defined
too much php
+2  A: 

The most natural way would probably be to evaluate the expressions on prior lines.

a = foo()
b = bar()
if a and b:
    ...

as solutions like all([5 < x, x < y]) hide that the side effects are important and solutions using bitwise and (&) seem subtle and misusing—both of these would require a comment in your code to make it obvious you are forcing evaluation and will cause people reading your code to think What was he thinking???. Putting important calculations on their own lines makes more sense than hiding them within subtle, at-first-glance ugly code.

Though my solution doesn't prevent a NameError if b does not exist (i.e., you have a typo) and a is false, this is something you should be able to figure out by reading your code and using a bugfinder if you choose.

Mike Graham
+2  A: 

If it's just the possibility of programmer-error you want to preclude, eagerly evaluating expressions won't do much. For instance, mistakenly doing x or y() instead of x() or y() won't be detected. Perhaps you're actually looking for tools like pylint, pyflakes or pychecker.

Thomas Wouters
Yes, but I need to do this dynamically, and this tools seem to work like command-line executables. Any framwork/module you know that does the same given a ´str´?
Manuel
A: 

If you are receiving the statement from the user and want to execute it with your own semantics, you should parse it yourself with a tool such as pyparsing. It is messy and insecure to evaluate someone else's code in the middle of yours, mixing their results with yours and it is confusing to evaluate what looks to be Python code but with different semantics.

Mike Graham