5

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?

2
  • 4
    If you have numpy installed you can use numpy.logical_and() Commented Sep 16, 2013 at 19:23
  • 1
    X and Y means if the first item is truthy (which a non-empty list is), evaluate to the value of the second item. Commented Sep 16, 2013 at 20:36

3 Answers 3

16

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)) 
Sign up to request clarification or add additional context in comments.

22 Comments

+1 for functional way, and it's shorter than list comprehension and it's not building new list with zip (although it's possible to use izip)
map should be avoided in cases like this simply because list comprehension is easier to read. Functional way may be really great but i would not reccomend it in this kinds of situation (it's python!). artima.com/weblogs/viewpost.jsp?thread=98196
I don't know what's with the sudden trend in "must be a list-comp" over a builtin.... but this is how I'd do it... The only difference is that instead of truncating one sequence, it'll pad with None so you'll end up with padded False values - so functionally they're different
@Marcin and Jon - to quote Guido in PEP 279 - "filter and map should die and be subsumed into list comprehensions" - I believe they're trying to remove these functional constructs from future python versions.
@Marcin I agree Guido's word isn't gospel, but I do think he has a pretty good grasp on what should and shouldn't be part of Python. The Zen of Python also says "there should be one (and only one) obvious way to do something", and I think list comprehensions are more obvious and readable. However, map and filter have their place.
|
9

All 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)] 

1 Comment

it could be even better if you use itertools.izip
0

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] 

6 Comments

Something that "that looks a bit like 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.
@Marcin: Fair point. I did have that at the end but did not in the function. Thanks!
@Marcin: 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.
Apart from 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.
|

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.