-1

I am trying to add multiple 'or' clauses to a python if statement using list comprehension. My code is shown below. I would like to keep the list comprehension. In terms of pseudocode, the logic would simply be:

Alive_Beatles = each name that contains '(Beatle)' and either ('Paul', 'Yoko' or 'Ringo')

The code only returns Paul and skips Ringo and Yoko.

Names = ["John Lennon (Beatle)",  "Paul McCartney (Beatle)", "Ringo Starr (Beatle)", "Yoko Ono (Beatle)", "Mick Jagger (Rolling Stone)", "Brian Jones (Rolling Stone)", "Alex Jones (na)", "Adam Smith (na)"]
Alive_Beatles = [n for n in Names if ("Beatle" and ("Paul" or "Ringo" or "Yoko")) in n]

print Alive_Beatles
smci
  • 32,567
  • 20
  • 113
  • 146
Dongs14173
  • 185
  • 4
  • 14
  • https://stackoverflow.com/questions/15112125/how-do-i-test-multiple-variables-against-a-value – timgeb Dec 29 '17 at 11:12
  • Closed as dupe, reopened because of the second question, wish I had not reopened. Need coffee. – timgeb Dec 29 '17 at 11:14
  • 2
    @timgeb Cheers, go get your coffee. – cs95 Dec 29 '17 at 11:17
  • The title of this question is very misleading, esp. given that it was closed in favor of something non-list-comprehension-related. What to do? – smci Jul 30 '18 at 04:14
  • I fixed the title to reflect the actual question. I guess we can leave it closed, although I don't think it's an exact dupe and the answers here are better. – smci Jul 30 '18 at 05:25

3 Answers3

3

You need to test each name explicitly if it's in n:

[n for n in Names if ("Beatle" in n and ("Paul" in n or "Ringo" in n or "Yoko" in n))]

Otherwise the and and or use the truth value of you search strings (and each non-empty string is always True) and finally tests if Paul in n (the first truth value of the ors).

The documentation explicitly mentions this:

4.2. Boolean Operations — and, or, not

These are the Boolean operations, ordered by ascending priority:

Operation     Result                                Notes
x or y        if x is false, then y, else x         (1)
x and y       if x is false, then x, else y         (2)
not x         if x is false, then True, else False  (3)

Notes:

(1) This is a short-circuit operator, so it only evaluates the second argument if the first one is false.

(2) This is a short-circuit operator, so it only evaluates the second argument if the first one is true.

(3) not has a lower priority than non-Boolean operators, so not a == b is interpreted as not (a == b), and a == not b is a syntax error.

So "Beatle" and (...) evaluates according to (2) to the second argument because "Beatle" is truthy and according to (1) it evaluates to the first argument of the chained ors: "Paul" because it's also truthy.

MSeifert
  • 145,886
  • 38
  • 333
  • 352
0

This doesn't do what you expect because the expression

("Paul" or "Ringo" or "Yoko")

evaluates to "Paul". Type it in at an interpreter prompt to confirm this.

And even that only seems to work because

("Beatle" and ("Paul" or "Ringo" or "Yoko")) 

also evaluates to "Paul".

BoarGules
  • 16,440
  • 2
  • 27
  • 44
0

The most straightforward solution is to just list

[n for n in Names if "Beatle" in n and ("Paul" in n or "Ringo" in n or "Yoko" in n)]

But you could use any to get a bit closer to what you tried initially:

[n for n in Names if "Beatle" in n and any(x in n for x in ("Paul", "Ringo", "Yoko"))]
Jieter
  • 4,101
  • 1
  • 19
  • 31