Spot any errors? let me know, but Unleash your pedant politely please.

Friday, 29 May 2009

Exploiting Python's logical evaluation order for conditional evaluation in list comprehensions

My head is hurting slightly over this one. First I'll show the experimentation (edited!)
>>> if 'arse':5
...
5
>>> if '':5
...
>>>
>>> if 'arse'==True:5
...
>>> 'arse' and 'arse'
'arse'
>>> 'arse' and 'flange'
'flange'
>>> 'arse'==True
False
>>> a = [1,2,3,4,5]
>>> b = [4,4,4,4,5]
>>> c = [x==y for x,y in zip(a,b)]
>>> c
[False, False, False, True, True]
>>> a = [(x<y and x+1 or y) for x,y in zip(a,b)]
>>> a
[2, 3, 4, 4, 5]
>>> a = [(x<y and x+1 or y) for x,y in zip(a,b)]
>>> a
[3, 4, 4, 4, 5]
>>> a = [(x<y and x+1 or y) for x,y in zip(a,b)]
>>> a
[4, 4, 4, 4, 5]
>>> a = [(x<y and x+1 or y) for x,y in zip(a,b)]
>>> a
[4, 4, 4, 4, 5]

I then spent a little time reading about logical operator and precedence in my Python Pocket Reference, because this was messing with my head. In x and y, if x is false, y is not evaluated. If x is true in some form, then y gets evaluated. If y happens to be a string, then the evaluation of y is returned, which is a string.

What we have then is a sort of trick if:else: statement. By writing…
condition and x or y

…we're achieving the same as…
if condition:
x
else:
y

This is remarkably handy stuff. Especially (as shown above) when used in a list comprehension. I'm not sure that it's particularly readable, but it's certainly more concise than the code I had previoulsy. More importantly, it works too (the previous code did not). The result is this (names changed to protect the innocent), which increments the values in a list, but only up to the corresponding limits specified in another list:
counters = [counter<boundary.upper and counter+1 or counter for counter,boundary in zip(counters,boundaries)]

The code I had in place prior looked something like this:
newList=[]
for counter,limit in zip(counters, limits):
if counter < limit.upperLimit:
newList.append[counter+1]
else:
newList.append[counter]
counters=newList

2 comments:

  1. Nice, but Python has this already built in as
    x if condition else y
    so no tricks exploiting the short-circuiting
    evaluation of logical expressions are needed.

    ReplyDelete
  2. I didn't see this comment when it was posted, but thanks Chris !

    ReplyDelete