Comparison operators and 'is' - operator precedence in python? - python

So I was looking at some code online and I came across a line (at line 286):
if depth > 0 and best <= -MATE_VALUE is None and nullscore > -MATE_VALUE:
The part I had trouble understanding was the best <= -MATE_VALUE is None.
So I fired up the interpreter to see how a statement such as value1 > value2 is value3 work. So I tried
>>> 5 > 2 is True
False
>>> (5 > 2) is True
True
>>> 5 > (2 is True)
True
My Question
Why is 5 > 2 is True not True? And how do these things generally work?
Thanks.

You're seeing python's operator chaining working
5 > 2 is True
Is equivalent to
5>2 and 2 is True
You can see this in that
>>> 5>2 is 2
Returns True.

First, 5 > 2 is True is equivalent to (5 > 2) and (2 is True) because of operator chaining in python (section 5.9 here).
It's clear that 5 > 2 evaluates to True. However, 2 is True will evaluate to False because it is not implicitly converted to bool. If you force the conversion, you will find that bool(2) is True yields True. Other statements such as the if-statement will do this conversion for you, so if 2: will work.
Second, there is an important difference between the is operator and the == operator (taken from here):
Use is when you want to check against an object's identity (e.g.
checking to see if var is None). Use == when you want to check
equality (e.g. Is var equal to 3?).
>> [1,2] is [1,2]
False
>> [1,2] == [1,2]
True
While this does not have an immediate impact on this example, you should keep it in mind for the future.

Related

Trying to make a program in Python that plays a note based on user input. But whenever I input a single note, it plays them all in order. Why? [duplicate]

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.

Logical operators in Python [duplicate]

This question already has answers here:
How do "and" and "or" act with non-boolean values?
(8 answers)
Closed last month.
While reading about logical operators in python, I came across the following expressions:
5 and 1
output: 1
5 or 1
output: 5
Can anyone explain how this is working?
I know that the operands of the logical operators are Boolean
that is well documented:
x or y if x is false, then y, else x
x and y if x is false, then x, else y
both short-circuit (e.g. or will not evaluate y if x is truthy).
the documentation also states what is considered falsy (False, 0, None, empty sequences/mappings, ...) - everything else is considered truthy.
a few examples:
7 and 'a' # -> 'a'
[] or None # -> None
{'a': 1} or 'abc'[5] # -> {'a': 1}; no IndexError raised from 'abc'[5]
False and 'abc'[5] # -> False; no IndexError raised from 'abc'[5]
note how the last two show the short-circuit behavior: the second statement (that would raise an IndexError) is not executed.
your statement that the operands are boolean is a bit moot. python does have booleans (actually just 2 of them: True and False; they are subtypes of int). but logical operations in python just check if operands are truthy or falsy. the bool function is not called on the operands.
the terms truthy and falsy seem not to be used in the official python documentation. but books teaching python and the community here do use these terms. there is a discussion about the terms on english.stackexchange.com and also a mention on wikipedia.
This is because of the short-circuit evaluation method.
For the and, all of the clauses must be True, so all of them must be evaluated. Once a False is encountered, the whole thing evaluates to False, we don't even need to evaluate the next ones.
>>> 1 and 2
2
>>> 2 and 1
1
>>> 1 and 2 and 3
3
>>> 1 and 0 and 2
0
>>> 0 and 1 and 2
0
But for or, any of the clauses being evaluated to True is enough for the whole thing to be True. So once it finds something to be True, the value of the whole thing is decided to be True, without even evaluating the subsequent clauses.
>>> 0 or 1
1
>>> 1 or 0
1
>>> 1 or 2
1
>>> 2 or 1
2
>>> 0 or 0 or 1
1
>>> 0 or 1 or 2
1
This is called short circuiting or lazy evaluation. Example, if you have "a or b" and a meets the criteria, then python will output it. Conversely, if you have "a and b" and "a" does not meet the criteria, then python will stop evaluating it since it cannot be satisfied.
In the first case when the and keyword is there, it is the second value that determines whether it is logically true or not, provided the first number is non-zero. Thus the second value is printed eventually.
In the second case, the keyword is or so when the first non-zero operand is encountered, since it makes the statement logically true, it is printed onto the output screen.
It is an example of lazy evaluation.

What should be simple expression (containing less_than, is, and) result? Why? [duplicate]

As expected, 1 is not contained by the empty tuple
>>> 1 in ()
False
but the False value returned is not equal to False
>>> 1 in () == False
False
Looking at it another way, the in operator returns a bool which is neither True nor False:
>>> type(1 in ())
<type 'bool'>
>>> 1 in () == True, 1 in () == False
(False, False)
However, normal behaviour resumes if the original expression is parenthesized
>>> (1 in ()) == False
True
or its value is stored in a variable
>>> value = 1 in ()
>>> value == False
True
This behaviour is observed in both Python 2 and Python 3.
Can you explain what is going on?
You are running into comparison operator chaining; 1 in () == False does not mean (1 in ()) == False.
Rather, comparisons are chained and the expression really means:
(1 in ()) and (() == False)
Because (1 in ()) is already false, the second half of the chained expression is ignored altogether (since False and something_else returns False whatever the value of something_else would be).
See the comparisons expressions documentation:
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).
For the record, <, >, ==, >=, <=, !=, is, is not, in and not in are all comparison operators (as is the deprecated <>).
In general, don't compare against booleans; just test the expression itself. If you have to test against a boolean literal, at least use parenthesis and the is operator, True and False are singletons, just like None:
>>> (1 in ()) is False
True
This gets more confusing still when integers are involved. The Python bool type is a subclass of int1. As such, False == 0 is true, as is True == 1. You therefor can conceivably create chained operations that almost look sane:
3 > 1 == True
is true because 3 > 1 and 1 == True are both true. But the expression:
3 > 2 == True
is false, because 2 == True is false.
1 bool is a subclass of int for historic reasons; Python didn't always have a bool type and overloaded integers with boolean meaning just like C does. Making bool a subclass kept older code working.

How does python interpret "True != True != True"? [duplicate]

As expected, 1 is not contained by the empty tuple
>>> 1 in ()
False
but the False value returned is not equal to False
>>> 1 in () == False
False
Looking at it another way, the in operator returns a bool which is neither True nor False:
>>> type(1 in ())
<type 'bool'>
>>> 1 in () == True, 1 in () == False
(False, False)
However, normal behaviour resumes if the original expression is parenthesized
>>> (1 in ()) == False
True
or its value is stored in a variable
>>> value = 1 in ()
>>> value == False
True
This behaviour is observed in both Python 2 and Python 3.
Can you explain what is going on?
You are running into comparison operator chaining; 1 in () == False does not mean (1 in ()) == False.
Rather, comparisons are chained and the expression really means:
(1 in ()) and (() == False)
Because (1 in ()) is already false, the second half of the chained expression is ignored altogether (since False and something_else returns False whatever the value of something_else would be).
See the comparisons expressions documentation:
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).
For the record, <, >, ==, >=, <=, !=, is, is not, in and not in are all comparison operators (as is the deprecated <>).
In general, don't compare against booleans; just test the expression itself. If you have to test against a boolean literal, at least use parenthesis and the is operator, True and False are singletons, just like None:
>>> (1 in ()) is False
True
This gets more confusing still when integers are involved. The Python bool type is a subclass of int1. As such, False == 0 is true, as is True == 1. You therefor can conceivably create chained operations that almost look sane:
3 > 1 == True
is true because 3 > 1 and 1 == True are both true. But the expression:
3 > 2 == True
is false, because 2 == True is false.
1 bool is a subclass of int for historic reasons; Python didn't always have a bool type and overloaded integers with boolean meaning just like C does. Making bool a subclass kept older code working.

Why do 'and' & 'or' return operands in Python?

I'm going through the LPTHW and I came across something I cannot understand. When will it ever be the case that you want your boolean and or or to return something other than the boolean? The LPTHW text states that all languages like python have this behavior. Would he mean interpreted vs. compiled languages or duck typed vs static typed languages?
I ran the following code:
>>> False and 1
False
>>> True and 1
1
>>> 1 and False
False
>>> 1 and True
True
>>> True and 121
121
>>> False or 1
1
>>> False or 112
112
>>> False or "Khadijah"
'Khadijah'
>>> True and 'Khadijah'
'Khadijah'
>>> False or 'b'
'b'
>>> b = (1, 2, "K")
>>> b
(1, 2, 'K')
>>> False or b
(1, 2, 'K')
>>>
Please help me understand whats going on here.
According to the documentation: http://docs.python.org/2/library/stdtypes.html
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.)
According to LPTHW: http://learnpythonthehardway.org/book/ex28.html
Why does "test" and "test" return "test" or 1 and 1 return 1 instead of True?
Python and many languages like it return one of the operands to their boolean expressions rather than just True or False. This means that if you did False and 1 you get the first operand (False) but if you do True and 1 your get the second (1). Play with this a bit.
I think you're somehow confused about what the docs says. Take a look at these two docs sections: Truth Value Testing and Boolean Operators. To quote the last paragraph on the fist section:
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.)
As you can see, you're right about operations and built-in functions but see the Important exception part, it is well stated that the Boolean operators will return one of their operands.
Now, what they can return depends hardly on the operator's short circuit logic. For or operator, it will return the first truthy value in the expression, since when it finds one, the whole expression is true. In case of every operand being falsey, or will return the last operand, meaning that it iterated over every one of them not being able to find a truthy one.
For and operator, if the expression is true, it will return the last operand, if the expression is false, it will return the first falsey operand. You can read more about Short Circuit Evaluation at the Wikipedia Page.
You have a lot of examples in your question, let's analyze some of them:
>>> False and 1 # return false (short circuited at first falsey value)
False
>>> True and 1 # return 1 (never short circuited and return the last truthy value)
1
>>> 1 and False # return false (short circuited at first falsey value, in this case the last operand)
False
>>> 1 and True # return True (never short circuited and return the last truthy value)
True
>>> True and 121 # return 121 (never short circuited and return the last truthy value)
121
>>> False or 1 # return 1 (since the first operand was falsey, or kept looking for a first truthy value which happened to be the last operator)
1
>>> False or 112 # return 112 for same reason as above
112
>>> False or "Khadijah" # return "Khadijah" for same reason as above
'Khadijah'
>>> True and 'Khadijah' # return "Khadijah" because same reason as second example
'Khadijah'
I think this should make a point. To help you further understand why this is useful, consider the following example:
You have a function that randomly generate names
import random
def generate_name():
return random.choice(['John', 'Carl', 'Tiffany'])
and you have a variable that you don't know if it has assigned a name yet so instead of doing:
if var is None:
var = generate_name()
You can do oneliner:
var = var or generate_name()
Since None is a falsey value, or will continue its search and evaluate second operand, this is, call the function ultimately returning the generated name. This is a very silly example, I have seen better usages (although not in Python) of this kind of style. I couldn't come out with a better example right now. You can also take a look at this questions, there are very useful answers on the topic: Does Python support short-circuiting?
Last but not least, this has nothing to do with static typed, duck typed, dynamic, interpreted, compiled, whatever language. It's just a language feature, one that might come handy and that is very common since almost every programming language I can think of provide this feature.
Hope this helps!
One would want and and or to evaluate to an operand (as opposed to always evaluating to True or False) in order to support idioms like the following:
def foo(self):
# currentfoo might be None, in which case return the default
return self.currentfoo or self.defaultfoo()
def foobar(self):
# foo() might return None, in which case return None
foo = self.foo()
return foo and foo.bar()
You can of course question the value of such idioms, especially if you aren't used to them. It's always possible to write equivalent code with an explicit if.
As a point against them, they leave some doubt whether the full range of falsey values is possible and intentionally accounted for, or just the one mentioned in the comment (with other falsey values not permitted). But then, this is true in general of code that uses the true-ness of a value that might be something other than True or False. It occasionally but rarely leads to misunderstandings.
This has to do with the way short circuit effect is implemented in Python.
With and (remember True and X = X), the result of the right expression is pushed into the stack, if it's false, it gets poped immediately, else the second expression is poped:
>>> import dis
>>>
>>> dis.dis(lambda : True and 0)
1 0 LOAD_CONST 2 (True)
3 JUMP_IF_FALSE_OR_POP 9
6 LOAD_CONST 1 (0)
>> 9 RETURN_VALUE
>>>
>>> True and 0
0
>>> 0 and True
0
>>>
similar to:
def exec_and(obj1, obj2):
if bool(obj1) != False:
return obj2
return obj1
With or, if the first expression is true, it gets popped immediately. If not, the second expression is poped, now the result really depends on the second.
>>> dis.dis(lambda : 0 or False)
1 0 LOAD_CONST 1 (0)
3 JUMP_IF_TRUE_OR_POP 9
6 LOAD_CONST 2 (False)
>> 9 RETURN_VALUE
>>>
>>> True or 0
True
>>> 1 or False
1
>>> False or 1
1
>>>
similar to:
def exec_or(obj1, obj2):
if bool(obj1) != True:
return obj2
return obj1
Consider the following use case:
element = dict.has_key('foo') and dict['foo']
Will set element to dict['foo'] if it exists, otherwise False. This is useful when writing a function to return a value or False on failure.
A further use case with or
print element or 'Not found!'
Putting these two lines together would print out dict['foo'] if it exists, otherwise it will print 'Not found!' (I use str() otherwise the or fails when element is 0 (or False) because that s considered falsey and since we are only printing it doesn't matter)
This can be simplified to
print dict.has_key('foo') and str(dict['foo']) or 'Not found!'
And is functionally equivalent to:
if dict.has_key('foo'):
print dict['foo']
else:
print 'Not found!'

Categories