tags:

views:

3259

answers:

5

I have a list of booleans in python. I want to AND (or OR or NOT) them and get the result. The following code works but is not very pythonic.

def apply_and(alist):
 if len(alist) > 1:
     return alist[0] and apply_and(alist[1:])
 else:
     return alist[0]

Any suggestions on how to make it more pythonic appreciated.

+28  A: 

all and any are all you need.

If you feel creative, you can also do:

import operator
def my_all(a_list):
  return reduce(operator.and_, a_list, True)

def my_any(a_list):
  return reduce(operator.or_, a_list, False)

keep in mind that those aren't evaluated in short circuit, whilst the built-ins are ;-)

another funny way:

def my_all_v2(a_list):
  return len(filter(None,a_list)) == len(a_list)

def my_any_v2(a_list):
  return len(filter(None,a_list)) > 0

and yet another:

def my_all_v3(a_list):
  for i in a_list:
    if not i:
      return False
  return True

def my_any_v3(a_list):
  for i in a_list:
    if i:
      return True
  return False

and we could go on all day, but yes, the pythonic way is to use all and any :-)

By the way, Python has not tail recursion elimination, so don't try to translate LISP code directly ;-)

fortran
Ants Aasma
luckly True and False (as the op wanted) are casted to 1 and 0 respectively, so the bitwise operators work as the logical ^_^
fortran
Worth noting... 2.5+ but very easy to backport.
Gregg Lind
+5  A: 

Reduce can do this:

reduce(lambda a,b: a and b, alist, True)

As fortran mentioned, all is the most succinct way to do it. But reduce answers the more general question "How to apply a logical operator to all elements in a python list?"

Frank Krueger
reduce isn't going away, AFAIK. it's being moved into the functools module, from its previous position in the global namespace
Eli Bendersky
@eliben: Why talk about Python 3 in future tense? *reduce is still there*. `reduce` is `functools.reduce` *in Python 3*
kaizer.se
+8  A: 

ANDing and ORing is easy:

>>> some_list = [True] * 100
# OR
>>> any(some_list)
True
#AND
>>> all(some_list)
True
>>> some_list[0] = False
>>> any(some_list)
True
>>> all(some_list)
False

NOTing is also fairly easy:

>>> [not x for x in some_list]
[True, False, False, False, False, False, False, False, False, False, False, False, False, False, False, False, False, False, False, False, False, False, False, False, False, False, False, False, False, False, False, False, False, False, False, False, False, False, False, False, False, False, False, False, False, False, False, False, False, False, False, False, False, False, False, False, False, False, False, False, False, False, False, False, False, False, False, False, False, False, False, False, False, False, False, False, False, False, False, False, False, False, False, False, False, False, False, False, False, False, False, False, False, False, False, False, False, False, False, False]

Of course, how you would use those results might require some interesting applications of DeMorgan's theorem.

Jason Baker
If you want short-circuting of the not variant, simply use generator expressions: `all(not x for x in some_list)` (but that is the same as `not any(some_list)` (quite a natural expression, huh?)).
kaizer.se
A: 

As the other answers show, there are multiple ways to accomplish this task. Here's another solution that uses functions from the standard library:

from functools import partial

apply_and = all
apply_or = any
apply_not = partial(map, lambda x: not x)

if __name__ == "__main__":
    ls = [True, True, False, True, False, True]
    print "Original: ", ls
    print "and: ", apply_and(ls)
    print "or: ", apply_or(ls)
    print "not: ", apply_not(ls)
mipadi
A: 

The idiom for such operations is to use the reduce function (global in Python 2.X, in module functools in Python 3.X) with an appropriate binary operator either taken from the operator module or coded explicitly. In your case, it's operator.and_

reduce(operator.and_, [True, True, False])
Eli Bendersky