== op isn't working as expected, don't know why [duplicate] - python

When I was looking at answers to this question, I found I didn't understand my own answer.
I don't really understand how this is being parsed. Why does the second example return False?
>>> 1 in [1,0] # This is expected
True
>>> 1 in [1,0] == True # This is strange
False
>>> (1 in [1,0]) == True # This is what I wanted it to be
True
>>> 1 in ([1,0] == True) # But it's not just a precedence issue!
# It did not raise an exception on the second example.
Traceback (most recent call last):
File "<pyshell#4>", line 1, in <module>
1 in ([1,0] == True)
TypeError: argument of type 'bool' is not iterable
Thanks for any help. I think I must be missing something really obvious.
I think this is subtly different to the linked duplicate:
Why does the expression 0 < 0 == 0 return False in Python?.
Both questions are to do with human comprehension of the expression. There seemed to be two ways (to my mind) of evaluating the expression. Of course neither were correct, but in my example, the last interpretation is impossible.
Looking at 0 < 0 == 0 you could imagine each half being evaluated and making sense as an expression:
>>> (0 < 0) == 0
True
>>> 0 < (0 == 0)
True
So the link answers why this evaluates False:
>>> 0 < 0 == 0
False
But with my example 1 in ([1,0] == True) doesn't make sense as an expression, so instead of there being two (admittedly wrong) possible interpretations, only one seems possible:
>>> (1 in [1,0]) == True

Python actually applies comparison operator chaining here. The expression is translated to
(1 in [1, 0]) and ([1, 0] == True)
which is obviously False.
This also happens for expressions like
a < b < c
which translate to
(a < b) and (b < c)
(without evaluating b twice).
See the Python language documentation for further details.

Related

Why do these shorthands not work with each other?

I was wondering how to take advantage of shorthand notation of if-else and += in Python of this simple expression:
I tried to set brackets everywhere and changed += to *= which didn't change the situation of course.
This works as expected:
a, b = 0, True
for i in range(123):
if b == True:
a = a + 1
Still working as expected, trying shorthand of if-else led me to:
a, b = 0, True
for i in range(123):
a = a + 1 if b == True else a
Finally the attempt to write:
a, b = 0, True
for i in range(123):
a += 1 if b == True else a:
fails and surprisingly I get pretty quickly huge integers for a
Moreover I'd really like something more shorthanded, e.g.:
a, b = 0, True
for i in range(123):
a += 1 if b
The for-loop needs to stay as it is, since in my case there are other operations that affect b.
Since noone seems to be posting, why it goes like this, here is mine - lines:
a = a + 1 if b == True else a
a += 1 if b == True else a
are seen by python as:
a = (a + 1 if b == True else a)
a += (1 if b == True else a)
This is why you get large numbers fast in second version - you will add a to a, when b is False. If you want to keep the if, then go:
a += (1 if b else 0)
Also don't compare b to True (or False), go foif b`, as it's more pythonic (it will prevent some weird mistakes, when other code will start to interact with yours).
EDIT: go for #Tomerikoo answer for even shorter code, but keep in mind, that those waters can be muddy and not everyone knows / easily follows, that adding boolean to int treats first as 1 (or 0 if False).
To closest to your proal is probably:
a, b = 0, True
for i in range(123):
a += b
Since bool is a subtype of int, no conversion is necessary.
You can do:
for i in range(123):
if b:a+=1
You can also do:
for i in range(123):
a = a + 1*b
Because booleans are ints:
>>> isinstance(True, int)
True
>>> True == 1
True
just note that
a += x if condition else y
will resolve to a += x if the condition is True; otherwise it will be a += y. this is why your numbers get big...
apart from that i suggest you use what U10-Forward's answer suggests.

Why does "1 in range(2) == True" evaluate to False? [duplicate]

This question already has an answer here:
Why does (1 in [1,0] == True) evaluate to False?
(1 answer)
Closed 5 years ago.
I came across this expression, which I thought should evaluate to True but it doesn't.
>> s = 1 in range(2)
>> s == True
>> True
Above statement works as expected but when this:
1 in range(2) == True
is executed, it evaluates to False.
I tried searching for answers but couldn't get a concrete one. Can anyone help me understand this behavior?
1 in range(2) == True is an operator chain, just like when you do 0 < 10 < 20
For it to be true you would need
1 in range(2)
and
range(2) == True
to be both true. The latter is false, hence the result. Adding parenthesis doesn't make an operator chaining anymore (some operators are in the parentheses), which explains (1 in range(2)) == True works.
Try:
>>> 1 in range(2) == range(2)
True
Once again, a good lesson learned about not equalling things with == True or != False which are redundant at best, and toxic at worst.
Try to write
(1 in range(2)) == True
It has to do with parsing and how the expression is evaluated.

python 3: Why do I get conflicting results when comparing to bool [duplicate]

Looking into Queue.py in Python 2.6, I found this construct that I found a bit strange:
def full(self):
"""Return True if the queue is full, False otherwise
(not reliable!)."""
self.mutex.acquire()
n = 0 < self.maxsize == self._qsize()
self.mutex.release()
return n
If maxsize is 0 the queue is never full.
My question is how does it work for this case? How 0 < 0 == 0 is considered False?
>>> 0 < 0 == 0
False
>>> (0) < (0 == 0)
True
>>> (0 < 0) == 0
True
>>> 0 < (0 == 0)
True
I believe Python has special case handling for sequences of relational operators to make range comparisons easy to express. It's much nicer to be able to say 0 < x <= 5 than to say (0 < x) and (x <= 5).
These are called chained comparisons. And that's a link to the documentation for them.
With the other cases you talk about, the parentheses force one relational operator to be applied before the other, and so they are no longer chained comparisons. And since True and False have values as integers you get the answers you do out of the parenthesized versions.
Because
(0 < 0) and (0 == 0)
is False. You can chain together comparison operators and they are automatically expanded out into the pairwise comparisons.
EDIT -- clarification about True and False in Python
In Python True and False are just instances of bool, which is a subclass of int. In other words, True really is just 1.
The point of this is that you can use the result of a boolean comparison exactly like an integer. This leads to confusing things like
>>> (1==1)+(1==1)
2
>>> (2<1)<1
True
But these will only happen if you parenthesise the comparisons so that they are evaluated first. Otherwise Python will expand out the comparison operators.
The strange behavior your experiencing comes from pythons ability to chain conditions. Since it finds 0 is not less than 0, it decides the entire expression evaluates to false. As soon as you break this apart into seperate conditions, you're changing the functionality. It initially is essentially testing that a < b && b == c for your original statement of a < b == c.
Another example:
>>> 1 < 5 < 3
False
>>> (1 < 5) < 3
True
>>> 0 < 0 == 0
False
This is a chained comparison. It returns true if each pairwise comparison in turn is true. It is the equivalent to (0 < 0) and (0 == 0)
>>> (0) < (0 == 0)
True
This is equivalent to 0 < True which evaluates to True.
>>> (0 < 0) == 0
True
This is equivalent to False == 0 which evaluates to True.
>>> 0 < (0 == 0)
True
Equivalent to 0 < True which, as above, evaluates to True.
Looking at the disassembly (the bytes codes) it is obvious why 0 < 0 == 0 is False.
Here is an analysis of this expression:
>>>import dis
>>>def f():
... 0 < 0 == 0
>>>dis.dis(f)
2 0 LOAD_CONST 1 (0)
3 LOAD_CONST 1 (0)
6 DUP_TOP
7 ROT_THREE
8 COMPARE_OP 0 (<)
11 JUMP_IF_FALSE_OR_POP 23
14 LOAD_CONST 1 (0)
17 COMPARE_OP 2 (==)
20 JUMP_FORWARD 2 (to 25)
>> 23 ROT_TWO
24 POP_TOP
>> 25 POP_TOP
26 LOAD_CONST 0 (None)
29 RETURN_VALUE
Notice lines 0-8: These lines check if 0 < 0 which obviously returns False onto the python stack.
Now notice line 11: JUMP_IF_FALSE_OR_POP 23
This means that if 0 < 0 returns False perform a jump to line 23.
Now, 0 < 0 is False, so the jump is taken, which leaves the stack with a False which is the return value for the whole expression 0 < 0 == 0, even though the == 0 part isn't even checked.
So, to conclude, the answer is like said in other answers to this question.
0 < 0 == 0 has a special meaning. The compiler evaluates this to two terms: 0 < 0 and 0 == 0. As with any complex boolean expressions with and between them, if the first fails then the second one isn't even checked.
Hopes this enlightens things up a bit, and I really hope that the method I used to analyse this unexpected behavior will encourage others to try the same in the future.
As other's mentioned x comparison_operator y comparison_operator z is syntactical sugar for (x comparison_operator y) and (y comparison_operator z) with the bonus that y is only evaluated once.
So your expression 0 < 0 == 0 is really (0 < 0) and (0 == 0), which evaluates to False and True which is just False.
maybe this excerpt from the docs can help:
These are the so-called “rich
comparison” methods, and are called
for comparison operators in preference
to __cmp__() below. The correspondence
between operator symbols and method
names is as follows: x<y calls
x.__lt__(y), x<=y calls x.__le__(y),
x==y calls x.__eq__(y), x!=y and x<>y
call x.__ne__(y), x>y calls
x.__gt__(y), and x>=y calls
x.__ge__(y).
A rich comparison method may return
the singleton NotImplemented if it
does not implement the operation for a
given pair of arguments. By
convention, False and True are
returned for a successful comparison.
However, these methods can return any
value, so if the comparison operator
is used in a Boolean context (e.g., in
the condition of an if statement),
Python will call bool() on the value
to determine if the result is true or
false.
There are no implied relationships
among the comparison operators. The
truth of x==y does not imply that x!=y
is false. Accordingly, when defining
__eq__(), one should also define __ne__() so that the operators will behave as expected. See the paragraph
on __hash__() for some important notes
on creating hashable objects which
support custom comparison operations
and are usable as dictionary keys.
There are no swapped-argument versions
of these methods (to be used when the
left argument does not support the
operation but the right argument
does); rather, __lt__() and __gt__()
are each other’s reflection, __le__()
and __ge__() are each other’s
reflection, and __eq__() and __ne__()
are their own reflection.
Arguments to rich comparison methods
are never coerced.
These were comparisons but since you are chaining comparisons you should know that:
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.
Here it is, in all its glory.
>>> class showme(object):
... def __init__(self, name, value):
... self.name, self.value = name, value
... def __repr__(self):
... return "<showme %s:%s>" % (self.name, self.value)
... def __cmp__(self, other):
... print "cmp(%r, %r)" % (self, other)
... if type(other) == showme:
... return cmp(self.value, other.value)
... else:
... return cmp(self.value, other)
...
>>> showme(1,0) < showme(2,0) == showme(3,0)
cmp(<showme 1:0>, <showme 2:0>)
False
>>> (showme(1,0) < showme(2,0)) == showme(3,0)
cmp(<showme 1:0>, <showme 2:0>)
cmp(<showme 3:0>, False)
True
>>> showme(1,0) < (showme(2,0) == showme(3,0))
cmp(<showme 2:0>, <showme 3:0>)
cmp(<showme 1:0>, True)
True
>>>
I'm thinking Python is doing it's weird between magic. Same as 1 < 2 < 3 means 2 is between 1 and 3.
In this case, I think it's doing [middle 0] is greater than [left 0] and equal to [right 0]. Middle 0 is not greater than left 0, so it evaluates to false.

What does if-if-else one-line do in Python? [duplicate]

This question already has answers here:
Does Python have a ternary conditional operator?
(31 answers)
Closed 6 years ago.
I ran into this expression in a question here about knapsack problems:
def f(v, i, S):
if i >= len(v): return 1 if S == 0 else 0
count = f(v, i + 1, S)
count += f(v, i + 1, S - v[i])
return count
When I try to write out line two if i >= len(v): return 1 if S == 0 else 0 in a more general form I get an error:
In [3]: if test1 : print x if test2 else print y
File "<ipython-input-3-9d4131fa0c48>", line 1
if test1 : print x if test2 else print y
^
SyntaxError: Missing parentheses in call to 'print'
Here is a generalized form:
In [16]: if True : print("first") if True else print("second")
first
In [17]: if True : print("first") if False else print("second")
second
In [18]: if False : print("first") if True else print("second")
[nothing]
In [19]: if False : print("first") if False else print("second")
[nothing]
What do you call this?
I'm surprised you can just take out the second positive case for if...then...else and turn it into if...else.
UPDATE: Sorry bout the python3 noob mistake, I just wasn't paying attention. As noted, the answers don't make sense without the mistake, so I've striked out the erroneous code.
You have found the ternary operator, which is known as a Conditional Expression in Python. The expression x if condition else y means that if the condition (which can be a complex statement or function) evaluates to True, the expression returns x, and if the condition evaluates to False, the expression returns y.
It works like the following if-statement:
if test1:
if test2:
print(x)
else:
print(y)
Your error stems from not wrapping the print function arguments in a parentheses. This is a change made to Python 3, whereas in Python 2, your syntax would have been fine. Rewrite it to look like:
if test1: print(x if test2 else y)
and your error will go away.
I feel it is important to point out that what you describe (if-if-else) is not a conditional expression per se, but it does include one:
1 if S == 0 else 0 is a conditional expression
if i >= len(v): return 1 if S == 0 else 0 is a compound statement which comprises a simple if statement with a conditional expression.
So, if the first if evaluates to True, then the conditional expression will be evaluated and the appropriate element (in this case 1 or 0) returned to the preceding statement (return, here).
It is an if-expression: a if condition else b means: if condition is true, then the expression has value a, otherwise b
Your problem is unrelated, Seems you are using python 3, you got the example from python 2. In python 3 print is a function, so just add parentheses
if you are using python of version >3, then print should have parenthesis i.e ()

What are the rules regarding chaining of "==" and "!=" in Python

This morning, I find myself writing something like:
if (a == b == c):
# do something
And was surprised that it gave me the expected result.
I thought it would behave as:
if ((a == b) == c):
# do something
But it obviously didn't. It seems Python is treating the first statement differently from the second, which is nice but I couldn't find any documentation or explanation regarding this.
I tested and got this:
In [1]: 2 == 2 == 2
Out[1]: True
In [2]: (2 == 2) == 2
Out[2]: False
Would someone care to explain me what are the rules regarding such "chaining" of == (or !=) ?
Thank you very much.
This works with all comparison operators - eg, you can also do:
>>> 4 < 5 < 6
True
>>> 4 < 5 !=2
True
In general, according to the documentation, a op1 b op2 c where op1 and op2 are any of: <, >, !=, ==, <=, >=, is , is not, in or not in will give the same result as:
a op1 b and b op2 c
The docs also say that this can work with arbitrarily many comparisons, so:
>>> 5 != '5' != 'five' != (3+2)
True
Which can be a slightly confusing result sometimes since it seems to say 5 != (3+2) - each operand is only compared with the ones immediately adjacent to it, rather than doing all possible combinations (which mightn't be clear from examples using only ==, since it won't affect the answer if everything defines __eq__ sanely).
As far as I know the example you point out isn't chaining.
2 == 2 == 2 is like (2 == 2) and ( 2 == 2) which turns out to be True and True
while
(2 == 2) == 2 is like (True) == 2
Check here: http://docs.python.org/reference/expressions.html#not-in

Categories

Resources