22

I am having trouble with the following logic:

Lets say I have a list:

L = ['a', 'b', 'c']

Both items are in the list...

if ('a' or 'b') in L:
    print "It's there!"
else:
    print 'No sorry'

prints It's there!


Only the first item is in the list...

if ('a' or 'd') in L:

prints It's there!


Neither item in the list...

if ('e' or 'd') in L:

prints No sorry


Here's the confusing one. Only the second item in the list...

if ('e' or 'a') in L:

prints No sorry


I do not understand why this is not registering as a true statement. How does this generalize to an or statement with n conditionals?

wjandrea
  • 28,235
  • 9
  • 60
  • 81
Brian Leach
  • 3,974
  • 8
  • 36
  • 75

4 Answers4

40

Let's break down the expression:

('e' or 'a') will first check if 'e' is True. If it is, the expression will return 'e'. If not, it will return 'a'.

Since all non-empty strings returns True, this expression will always return 'e'. This means that if ('e' or 'a') in L: can be translated to if 'e' in L, which in this case is False.

A more generic way to check if a list contains at least one value of a set of values, is to use the any function coupled with a generator expression.

if any(c in L for c in ('a', 'e')):
Steinar Lima
  • 7,644
  • 2
  • 39
  • 40
16

Use this instead:

 if 'a' in L or 'b' in L:

If we want to check if all these of this "items" are in the list, all and a generator comprehension is your friend:

items = 'a', 'b', 'c'
if all(i in L for i in items):

Or if any of these items are in the list, use any:

if any(i in L for i in items)
aIKid
  • 26,968
  • 4
  • 39
  • 65
3

Strings (except an empy string) will always evaluate to True when they are evaluated as a boolean. While evaluating with or/and both will return True, but there is a little difference between them:

print 'a' or 'b'    # Output:  a
print 'a' and 'b'   # Output:  b

or: will return the first string and: will return the last string

When you do

if ('a' or 'b') in L:

, it will check 'a' or 'b' which is 'a' and then check if 'a' is in L. Something similar happens with the other cases (based on what I explained before).

So when you do

if ('e' or 'a') in L:

, 'e' or 'a' will evaluate to 'e' and therefore it will print 'No Sorry', because 'e' is not in L.

What you must do is compare whether elements are in the list separately:

if 'a' in L or 'b' in L:
if 'a' in L or 'd' in L:
if 'e' in L or 'd' in L:
if 'e' in L or 'a' in L:
Christian Tapia
  • 33,620
  • 7
  • 56
  • 73
1

The trick to the output you're getting is that and and or in Python always evaluate to one of their operands -- generally the one that had to be evaluated last to determine the truthiness of the operation:

1 or 2   # Returns 1 because since 1 is true, there's no need to
         # evaluate the second argument.
1 or 0   # Returns 1, same thing.
0 or 2   # Returns 2 because 0 is false, so we need to evaluate
         # the second arg to check whether the operation is true.
0 or ""  # Returns "" (both 0 and "" are false).

1 and 2     # Returns 2 because for an and operation to be true,
            # both its operands need to be checked for truthiness.
0 and 2     # Returns 0, because we know that if the first operand
            # is false, so is the whole operation.
0 and None  # Still returns 0, we don't even need to check the
            # second operand.

So when you're evaluating (1 or 2) in [1, 3, 5] (where in truth you want 1 in [1, 3, 5] or 2 in [1, 3, 5]), what really happens is (1 or 2) is evaluated to 1, and your operation becomes 1 in [1, 3, 5].

wjandrea
  • 28,235
  • 9
  • 60
  • 81
Max Noel
  • 8,810
  • 1
  • 27
  • 35