As far as I know, the in operator in Python can't be chained or at least I couldn't find any info on it, here is my problem
Here is the code
arr = [1, True, 'a', 2]
print('a' in arr in arr) # prints False
print(('a' in arr) in arr) # prints True
What I don't understand is the first print, I know in the second the first in returns True and then it check if True is in arr, but what about the first one? Does it check if 'a' is in arr and then if arr is in arr?
The premise is false; the in operator can be chained. See Comparisons in the docs:
comp_operator ::= "<" | ">" | "==" | ">=" | "<=" | "!="
| "is" ["not"] | ["not"] "in"
So, just as with any other chained comparison, a in b in c is equivalent to (a in b) and (b in c) (except that b is only evaluated once.
The reason 'a' in arr in arr is false is that arr in arr is false. The only time x in x is true is if x is type that does substring comparisons for __contains__ (like str or bytes), or if it's a container that actually contains itself (like lst = []; lst.append(lst)).
Related
This question already has answers here:
Unexpected result from `in` operator - Python [duplicate]
(3 answers)
Closed 2 years ago.
I found strange behavior with Python's in operator
d = {}
'k' in d == False # False!
I thought it's because of precedence:
('k' in d) == False # True, it's okay
'k' in (d == False) # Error, it's also okay
But, what precedence evaluates the following expression then?
d = {}
'k' in d == False
If it's because of wrong precedence why it doesn't fire an error like if:
'k' in (d == False)
In other words, what happens under the hood of Python with this expression?
'k' in d == False
in is considered a comparison operator, and so it is subject to comparison chaining.
'k' in d == False
is equivalent to
'k' in d and d == False
because both in and == are comparison operators.
You virtually never need direct comparison to Boolean literals, though. The "correct" expression here is 'k' not in d.
For reference, this is described in the Python documentation, under 6.10. Comparisons:
comparison ::= or_expr (comp_operator or_expr)*
comp_operator ::= "<" | ">" | "==" | ">=" | "<=" | "!="
| "is" ["not"] | ["not"] "in"
and
Comparisons can be chained arbitrarily, e.g., x < y <= z is equivalent to x < y and y <= z, except that y is evaluated only once (but in both cases z is not evaluated at all when x < y is found to be false).
This question already has answers here:
Check if a word exists in a dictionary doesn't find any word
(1 answer)
Is `a<b<c` valid python?
(2 answers)
Closed 5 years ago.
Python seems to evaluate oddly with (what I thought) would be relatively straightforward syntax. Can anyone shed light on what's happening behind the scenes here? What does python think is happening in the first case?
>>> x = 'foo'
>>> 'f' in x == True
False
>>> ('f' in x) == True
True
>>> 'f' in (x == True)
Traceback (most recent call last):
File "<stdin>", line 1, in <module>
TypeError: argument of type 'bool' is not iterable
What you see here is chained comparisons:
'f' in x == True
Both in and == are both comparisons. Now Python interprets chained comparison interpreters with an implicit and. So you have basically written:
'f' in x and x == True
and the second check fails.
The same occurs if you for instance write:
a < b in c
it is short for:
a < b and b in c
(except that expressions are only evaluated once).
If we take a look at the documentation, we see that there are 11 comparators:
comp_operator ::= "<" | ">" | "==" | ">=" | "<=" | "<>" | "!="
| "is" ["not"] | ["not"] "in"
and furthermore it states:
Comparisons can be chained arbitrarily, e.g., x < y <= z is
equivalent to x < y and y <= z, except that y is evaluated only
once (but in both cases z is not evaluated at all when x < y is
found to be false).
Formally, if a, b, c, ..., y, z are expressions and op1,
op2, ..., opN are comparison operators, then a op1 b op2 c ... y
opN z is equivalent to a op1 b and b op2 c and ... y opN z, except
that each expression is evaluated at most once.
I have trying to achieve the following. I wrote code to or the values of a string and count the number of 1's in the string.
a='11001'
b='00111'
a and b
'00111'
a or b
11001
Can someone explain how this works. The answer that I am expecting is that when I do a and b, the result should be 00001 and when I do a or b, I should get 11111, the logical and and or.
I do understand that non empty strings yield True for all cases. So I can explain the above behavior, thought I do not understand how to implement boolean and and or
Thanks in advance.
a='11001'
b='00111'
c = ''
case "and":
for bit_1, bit_2 in zip(a, b):
if bit_1 == bit_2:
c += '1'
else:
c += '0'
case "or":
for bit_1, bit_2 in zip(a, b):
if bit_1 == '1' or bit_2 == '1':
c += '1'
else:
c += '0'
The and keyword does not work like you expect. What it does is:
evaluate the left-hand operand.
if that operand is falsy (None, False, 0, empty string, …) then return it.
otherwise evaluate the right-hand operand and return it.
There, '11001' is not falsy, as it's a non-empty string so '11101' and '00111' returns the second string.
Maybe you wanted to do a bitwise operation, using the bitwise and operator, &?
>> 0b11001 & 0b00111
1
and and or keywords work on Boolean values. A non-empty string is True in python.
If you want to perform bit operations, you can use something like this:
>>> a='11001'
>>> b='00111'
>>> bin(int(a, 2) & int(b, 2))
'0b1'
>>> bin(int(a, 2) | int(b, 2))
'0b11111'
And to count the number of 1's in the or result, you can use the count() function:
>>> bin(int(a, 2) | int(b, 2))[2:]
'11111'
>>> bin(int(a, 2) | int(b, 2))[2:].count('1')
5
list1 = ['Hello', 10, None]
list2 = [g.lower() for g in list1 if isinstance(g, str)]
list3 = [g.lower() if isinstance(g,str) else g for g in list1]
list4 = [isinstance(g, str) and g.lower() or g for g in list1]
If I want to convert the string in list to lowercase, I can use the method in list2 and the output will be ['hello'].
In addition to this conversion, if I want to keep integers (which is 10 in this case) and None, the methods in both list3 and list4 will work and the output will be ['hello', 10, None].
My question is that I can't understand how the method in list4 works.
To start, writing code like this:
condition and value1 or value2
was how people implemented a ternary conditional operator in Python before the:
value1 if condition else value2
conditional expression was introduced in version 2.5 because of PEP 0308. Using the old method is now deprecated in favor of the slightly more efficient and far more readable newer method.
The old method works because of how and and or operate in Python. Instead of returning boolean results like in most other languages, these operators return values.
Doing a and b returns a if a evaluates to False; otherwise, it returns b:
>>> 0 and 1
0
>>> 1 and 0
0
>>> 1 and 2
2
>>>
Doing a or b returns a if a evaluates to True; otherwise, it returns b:
>>> 1 or 0
1
>>> 0 or 1
1
>>> 1 or 2
1
>>>
Also, in case you do not know, 0 evaluates to False while every other number evaluates to True.
Coming to your code, this:
isinstance(g, str) and g.lower() or g
is actually interpreted by Python like:
(isinstance(g, str) and g.lower()) or g
Now if isinstance(g, str) returns False (g is not a string):
(False and g.lower()) or g
False is returned by and:
False or g
and then or returns g. Thus, we avoided calling .lower() on a non-string type.
If however isinstance(g, str) returns True (g is a string):
(True and g.lower()) or g
and returns g.lower():
g.lower() or g
and then or returns g.lower(), which is fine because g is a string.
Summed up, these two expressions:
g.lower() if isinstance(g,str) else g
isinstance(g, str) and g.lower() or g
are functionally equivalent. But please use the first!! The other is terrible for readability.
Quoting the doc:
The expression x and y first evaluates x; if x is false, its
value is returned; otherwise, y is evaluated and the resulting value
is returned.
The expression x or y first evaluates x; if x is true, its value is
returned; otherwise, y is evaluated and the resulting value is
returned.
Because of precedence rules, isinstance(g, str) and g.lower() or g is actually evaluated as
(isinstance(g, str) and g.lower()) or g (multiplication is of higher precedence than addition).
That basically means the following:
if isinstance(g, str) is true, result of g.lower() will be taken
otherwise g will be taken
As you see, it's the same thing that you have in list3 operation.
Can someone explain how these results are possible (python 2.6):
>>> 1<3>2
True
>>> (1<3)>2
False
>>> 1<(3>2)
False
I would think that one of the last two would match the first one, but apparently the operators in the first statement is somehow linked?!
Your first example shows comparison chaining. 1<3>2 means 1<3 and 3>2 (except each expression is evaluated only once). This applies to all comparison operators in Python.
Your second two examples force one comparison to be evaluated first, resulting in a boolean value which is then compared with the remaining integer.
In your first case 1<3>2 1 is actually lesser than 3 and 3 is greater than 2, so True.
In your second case (1<3)>2 (1<3) evaluates as True that represented as 1, so 1 is not greater than 2.
In your third case 1<(3>2), 1 is not lesser than True that represented as 1.
The last two statements compare booleans against an integer:
>>> True > 2
False
>>> 1 < True
False
The first statement is comparison chaining, which works for all boolean comparisons in Python. Note from the documentation:
Comparisons yield boolean values: True or False.
By placing parts of your expression in brackets, those parts get evaluated first and you end up with comparing integers and booleans.
As per docs,
Unlike C, all comparison operations in Python have the same priority, which is lower than that of any arithmetic, shifting or bitwise operation. Also unlike C, expressions like a < b < c have the interpretation that is conventional in mathematics:
comparison ::= or_expr ( comp_operator or_expr )*
comp_operator ::= "<" | ">" | "==" | ">=" | "<=" | "<>" | "!="
| "is" ["not"] | ["not"] "in"
Comparisons yield boolean values: True or False.
Comparisons can be chained arbitrarily, e.g., x < y <= z is equivalent to x < y and y <= z, except that y is evaluated only once (but in both cases z is not evaluated at all when x < y is found to be false).