I have two lists:
X = [True,False] Y = [True,True] I am trying to compare X[0] with Y[0] and X[1] with Y[1].
I tried
in [7]: X and Y Out[7]: [True, True] but the result I was expecting was [True,False].
What should I be doing?
This is a perfect opportunity to use map, because and can be represented with a built-in function:
import operator X = [True,False] Y = [True,True] map(operator.and_, X,Y) #=> [True, False] The reason why you get the behaviour you did is that and performs operations on the operands as if they had bool applied to them. All non-empty lists evaluate to True in a boolean context.
As to the "list comprehension is always better" point: no it's not. The equivalent list comprehension is:
[x and y for x, y in zip(X, Y)] Which has to build an intermediate object (either list or generator, depending on the python version), and still requires the reader to know what zip does, just as much as map does. It's also likely to be slightly slower (because map + builtin function is fast - it all happens in the C layer, essentially). In fact, timeit shows that izip is faster (see below), but I really think the readability point is more important; you may also see different results if performance really matters.
>>> timeit.timeit('map(operator.and_, X,Y)', 'import operator; import itertools; import random; X = [random.choice([True,False]) for _ in range(1000)]; Y = [random.choice([True,False]) for _ in range(1000)]', number=10000) 1.0160579681396484 >>> timeit.timeit('[x and y for x, y in zip(X, Y)]', 'import operator; import itertools; import random; X = [random.choice([True,False]) for _ in range(1000)]; Y = [random.choice([True,False]) for _ in range(1000)]', number=10000) 1.3570780754089355 >>> timeit.timeit('[x and y for x, y in itertools.izip(X, Y)]', 'import operator; import itertools; import random; X = [random.choice([True,False]) for _ in range(1000)]; Y = [random.choice([True,False]) for _ in range(1000)]', number=10000) 0.965054988861084 That said, if you need arbitrary numbers of lists, you need to use all in a list comprehension (or combined with izip directly); and and_ is technically bitwise and, so be aware that might have funky results if working with numeric types other than bool.
Here is an all version:
import itertools map(all,itertools.izip(X,Y,Z)) zip (although it's possible to use izip)None so you'll end up with padded False values - so functionally they're differentAll non-empty lists evaluate to True in a boolean context, and and evaluates to the last expression it evaluated (Y in this case), which is why you get the result you do. You want something like this:
[x and y for x, y in zip(X, Y)] Suppose you have and arbitrary group of lists:
A=[True, False, False] B=[True, True, False] C=[3,0,0] Now write something that looks a bit like itertools.izip but allows a function to be added:
def elements(*iterables, **kwds): func=kwds.get('func', None) iterables=map(iter, iterables) while iterables: t=tuple(map(next, iterables)) if func is not None: yield func(t) else: yield t Now add functions that will return the logical result of F(A[0],B[0],C[0]...). For example, these each do the function described:
def ands(elements): ''' logical 'and' for all the elements''' return all(elements) def ors(elements): ''' logical 'or' for all the elements''' return any(elements) def bitand(elements): ''' bitwise 'and' for all the elements''' return reduce(operator.and_,elements) Then just call the function:
print list(elements(A,B,C,func=ands)) # [True, False, False] Or for you specific example:
print list(elements([True,False],[True,True],func=ands)) # [True, False] Or just use all directly:
print list(elements([True,False],[True,True],func=all)) # [True, False] itertools.izip but allows a function to be added" is itertools.imap.ands is just equivalent to all - your code just passes a generator which recapitulates the underlying sequences. Likewise with ors and any.Something that "that looks a bit like itertools.izip but allows a function to be added" is itertools.imap Not quite true. You would need to do list(itertools.imap(all,itertools.izip(X,Y)) to get the same thing.izip not returning a list, your version just changes how the arbitrary number of arguments is passed to the mapping parameter function. You could equally well do itertools.imap(lambda *p: all(p) ,A,B). Either way, it's a hell of a lot less code than writing your own slightly different imap.
numpyinstalled you can usenumpy.logical_and()X and Ymeans if the first item is truthy (which a non-empty list is), evaluate to the value of the second item.