This question already has answers here:
How to test multiple variables for equality against a single value?
(31 answers)
Closed 5 years ago.
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
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.
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".
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"))]
Related
This question already has answers here:
"x not in y" or "not x in y"
(6 answers)
Closed 3 years ago.
The canonical question on the difference between not x in y and x not in y states that these two are equivalent in implementation (in CPython). The operator precedence rules state that the Boolean not has higher precedence than in. So shouldn't not x in y be implemented to be equivalent to (not x) in y?
Consider
x = False
list2 = ['a']
not x in list2
Out[19]: True
(not x) in list2
Out[20]: False
I would have expected second last statement to be False, if I'm understanding the precedence rules correctly.
Rather subtle but if you look in the official documentation that you linked to, you will see that "not in" is its own operator, which has a higher precedence than "not" and the same precedence as "in".
Your mistake is probably coming from reading the operator precedence rules the wrong way around. As it currently stands, the lowest precedence comes higher up in the list.
So not x in list2 is equvalent to not (x in list2).
Your last assumption will, because you have surrounded it with parentheses, first evaluate not x to a boolean (False), then check if that boolean is in the list (which it is not, there's only a string in the list), and return False.
Kindly help me understand why this works. The code below lists duplicates in an iterable. However, the use of the or operator behaves like the else in an if..else statement..
j = set()
my_list = [1, 2, 3 ,3 , 3 ,4, 4]
j_add = j.add
twice = set(x for x in my_list if x in j or j_add(x))
print list(twice)
Would expect the line to be:
twice = set(x for x in my_list if x in j else j_add(x))
Thought or returns a boolean not a value
The or operator returns the last evaluated argument, which may or may not be a Boolean.
This behavior is explained in the Documentation:
Note that neither and nor or restrict the value and type they return to False and True, but rather return the last evaluated argument. This is sometimes useful, e.g., if s is a string that should be replaced by a default value if it is empty, the expression s or 'foo' yields the desired value.
Of course, it helps to remember what is interpreted as false and what is interpreted as true:
[T]he following values are interpreted as false: False, None, numeric zero of all types, and empty strings and containers (including strings, tuples, lists, dictionaries, sets and frozensets). All other values are interpreted as true.
So in the expression:
A = B or C
As #MartijnPieters points out in a comment, an or expression short-circuits. If the first argument (B in this case) is interpreted as true, the entire expression must be true so the second argument (C) is never evaluated. Therefore the first argument (B) is "the last evaluated argument" and is what is returned. However, if the first argument (B) is interpreted as false, the second argument (C) must still be evaluated to determined the truthiness of the expression (no short-circuit takes place). In that case, "the last evaluated argument" is the second argument (C), which is returned regardless of whether the expression evaluates true or false.
It effectively accomplishes the same as the Conditional Expression:
A = B if B else C
However, Conditional Expressions were only added to Python in version 2.5, while the Boolean Operator behavior has existed from the beginning (or at least for a very long time). Most seasoned Python programmers will easily recognize and are in the habit of using A = B or C. Conditional Expressions are commonly reserved for more complex conditions that won't work with a simple or (for example in A = B if X else C the condition is not based on the truthiness of B but X, which could be anything from a simple value to a complex expression).
However, you need to be careful because, as JaredGoguen points out in his answer, changing the or to an else in the OP's sample actually changes the behavior of the code. That code was written to depend on this specific behavior of the or operator. You can't just replace any use of or for assignment with a Conditional Expression. Additional refactoring may be needed as well.
I might make a value judgment here and say that this is not good code because it is using the short-circuiting behavior of or to produce a side-effect.
Consider the given conditional: if x in j or j_add(x).
When x in j, the or short-circuits, skips the j_add(x) part of the conditional, and evaluates as True.
When x not in j, the statement j_add(x) is checked for its truthiness. This method returns None, which is falsy, and so or evaluate as False.
So, the entire conditional will evaluate the same as x in j. However j_add(x) has the side-effect of adding x to j! This side-effect is being exploited in order to record the unique members my_list in a quick-and-dirty comprehension.
Changing the or to an else would still construct j as desired, but it would inappropriately add None, the return value of j_add(x), to twice.
The differences' between the operator "==" and "=". When would each be used? Why would each be used?
In python and other languages like C,
"=" is a assignment operator and is used to assign a value to a variable.
Example: a=2 # the value of a is 2
whereas "==" is Comparison operator and is used to check whether 2 expressions give the same value .Equality check returns true if it succeeds and else return false.
Example: a=2 b=3 c=2
a==b (#false because 2 is not equal to 3)
a==c (#true because 2 is equal to 2)
= is used for assignment: e.g.: apple = 'apple'.
It states what is what.
== compares one value to another. Is 5 equal to 5 should be written like this: 5 == 5
An == expression evaluates to true, is an equality operator.
== has the value of two operands are equal make the condition or statement true.
= is an expression of assignment operator to the symbol of variables, arrays, objects.
Both operators are very important, and they work in different ways in every equivalent object. Their behavior in their operation is based on the identity of objects. Are reflation of their variables.
When using == in compares the values of two objects example having two cars from the same company and have the same identity and features and same looks.
The rule implies that the statement and condition to be trues
To use = operator is when to evaluate variables in an expression if both sides of the operator mean same or the objects are same if not same its expression will be false and if true the expressions or objects are same.
My first question is what is the more abstract question for the question: 'what is the operation that returns 6 for the expression (2 and 2*3)? Please feel free to retitle my question appropriately.
My second question is what is it that is going on in python that returns 6 for (2 and 2*3). There seems something elegant going on here, and I'd like to read up on this operation.
From the Python language reference:
Note that neither and nor or restrict the value and type they return to False and True, but rather return the last evaluated argument.
As such, 2 and 2*3 means it first evaluates bool(2), which evaluates to True, and then it evaluates bool(2*3) which evaluates to True. Therefore it'll return the last evaluated argument, which is 2*3 (6).
According to the python docs
x and y : if x is false, then x, else y
First you have to read this and then you have to read this. :)
After that you will know that and is a boolean operator, that tries to convert it's first operand to boolean. So if you read the first thing you will see that 2 is converted to True.
The third thing you need to know is that the and operand will return it's first argument if it is converted to False and it's second argument if it evaluates to True.
So basically
z = x and y
Can be translated to:
if x:
z = y
else:
z = x
And now you understand everything. :)
2 is evaluated to True and then and operator returns the value of it's second argument which is 6.
Applying lazy evaluation, python return for a and b a if a evaluates to False and b if a evaluates to True.
Hence 2 evaluates to True, 2 and 2*3 return 2*3 which equals 6.
Basically it's same as 2 and 6.
How it works? and returns first element if it's considered False (False, 0, [] ...) and return second otherwise
This is to do with how Python evaluates the expression x and y. It returns y if x is True, and x if x if False.
So, in case of 2 and 2 * 3, since 2 is evaluated to True, it would return the value 2 * 3, which is 6.
In case of and operation between multiple operands, it returns the 1st non-True value, and if all the values are True, it returns the last value.
Similarly, for or operator, the expression say, A or B or C, returns the 1st True value. And if all the values are False, it returns the last value.
I have some code here:
m = None
n = None
if not m:
print "Something happens"
>>> Something happens
if I do:
if not m and n:
print "Something happens"
Nothing happens.
But I can do:
m, n = 1,2
if m and n:
print "Something happens"
>>> Something happens
Why are if and if not handled the same way? Does 'if not', not take 'and' statements?
Thank you
You have an operator precedence problem.
if not m and n is equivalent to if (not m) and n. What you want is if not m and not n or if not (m or n).
See also: De Morgan's Laws
I think you are looking for this article: Truth Value Testing
Any object can be tested for truth value, for use in an if or while condition or as operand of the Boolean operations below. The following values are considered false:
None
False
zero of any numeric type, for example, 0, 0L, 0.0, 0j.
any empty sequence, for example, '', (), [].
any empty mapping, for example, {}.
instances of user-defined classes, if the class defines a __nonzero__() or __len__() method, when that method returns the integer zero or bool value False.
All other values are considered true — so objects of many types are always true.
Operations and built-in functions that have a Boolean result always return 0 or False for false and 1 or True for true, unless otherwise stated. (Important exception: the Boolean operations or and and always return one of their operands.)
Especially the next chapter explains your case (Boolean Operations — and, or, not):
not has a lower priority than non-Boolean operators
not applied to its closet operand. You wrote if not m and n where not applies to m which is True, and because of and, n is evaluate to False, hence the entire statement is evaluated to False.