Python - logical evaluation order in "if" statement - python

In Python we can do this:
if True or blah:
print("it's ok") # will be executed
if blah or True: # will raise a NameError
print("it's not ok")
class Blah:
pass
blah = Blah()
if blah or blah.notexist:
print("it's ok") # also will be executed
Can somebody point me to documentation on this feature?
Is it an implementation detail or feature of the language?
Is it good coding style to exploit this feature?

The or and and short circuit, see the Boolean operations documentation:
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.
Note how, for and, y is only evaluated if x evaluates to a True value. Inversely, for or, y is only evaluated if x evaluated to a False value.
For the first expression True or blah, this means that blah is never evaluated, since the first part is already True.
Furthermore, your custom Blah class is considered True:
In the context of Boolean operations, and also when expressions are used by control flow statements, the 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. (See the __nonzero__() special method for a way to change this.)
Since your class does not implement a __nonzero__() method (nor a __len__ method), it is considered True as far as boolean expressions are concerned.
In the expression blah or blah.notexist, blah is thus true, and blah.notexist is never evaluated.
This feature is used quite regularly and effectively by experienced developers, most often to specify defaults:
some_setting = user_supplied_value or 'default literal'
object_test = is_it_defined and is_it_defined.some_attribute
Do be wary of chaining these and use a conditional expression instead where applicable.

This is called short-circuiting and is a feature of the language:
http://docs.python.org/2/tutorial/datastructures.html#more-on-conditions
The Boolean operators and and or are so-called short-circuit operators: their arguments are evaluated from left to right, and evaluation stops as soon as the outcome is determined. For example, if A and C are true but B is false, A and B and C does not evaluate the expression C. When used as a general value and not as a Boolean, the return value of a short-circuit operator is the last evaluated argument.

It's the way the operators logical operators, specifically or in python work: short circuit evaluation.
To better explain it, consider the following:
if True or False:
print('True') # never reaches the evaluation of False, because it sees True first.
if False or True:
print('True') # print's True, but it reaches the evaluation of True after False has been evaluated.
For more information see the following:
The official python documentation on boolean operations
A stack overflow question regarding short circuitry in python

With the or operator, values are evaluated from left to right. After one value evaluates to True, the entire statement evaluates to True (so no more values are evaluated).
Official documentation
It's a feature of the language
There is nothing wrong with using its functionality

Related

Python checking if a variable is true without the == operator

I have recently stumbled upon this bit of code:
def equal_button_press(self):
if self.add_trigger or self.sub_trigger or self.mult_trigger or self.div_trigger or self.asin_trigger:
In the if statement there is no specification for the code to check whether any of the variables self.add_trigger, self.sub_trigger, self.mult_trigger, self.div_trigger or self.asin_trigger are true.
Does this mean that it is possible to check if the object is true without the == operator?
The == operator compares the value or equality of two objects, not if they are True. If the 2 objects are equal, then python will return True.
Here's a pretty great article going in depth about this: https://towardsdatascience.com/comparison-in-python-is-not-as-simple-as-you-may-think-a83eec69ab54
That being said, there are truthy and falsy values in Python:
Values that evaluate to False are considered Falsy.
Values that evaluate to True are considered Truthy.
Some truthy values include:
Non-empty sequences or collections
Numeric values that are not zero.
Some falsy values include:
Zero of any numeric type.
Empty sequences or collections
None and False
So the code is checking which values are truthy, and those values will be outputted.
With the Boolean OR operator, you can connect two Boolean expressions into one compound expression. At least one subexpressions must be true for the compound expression to be considered true, and it doesn’t matter which. If both subexpressions are false, then the expression is false.
The clause inside of an if statement to determine whether it will be executed or not is not specifically only done when a value is equal to true (In Python, True) but can also be triggered if the variable or logical statement returns any value that is not 'falsy'.
#sj95126 provided the exact answer in the comments that I was also going to recommend on this!
Documentation Reference: https://docs.python.org/3/library/stdtypes.html#truth-value-testing
Yes, it is possible to check if a value is True without the == operator. An if statement takes a boolean after the if keyword. The == operator checks if the 2 values on both sides are the same. If they are, it "returns" True. Otherwise, it "returns" False.
foo = True
# These if statments are the same
if foo == True:
print("foo is True")
if foo:
print("foo is True")

Order of evaluation of logical AND (python)

In python, which is more efficient:
if a:
if b:
# do something
or
if a and b:
# do something
The latter would be more efficient if b isn't calculated when a is false. But I can't seem to pinpoint whether that's the case in Python docs. Maybe someone can point me to it?
Short-Circuiting
Python doesn't run Y in X and Y if X is false. You can try it out yourself:
if True and print("Hello1"):
pass
if False and print("Hello2"):
pass # "Hello2" won't be printed
The reason Python does this is it has something called short-circuiting which optimising logic expressions like this one. Python realises that if if X is false then there is no point in checking Y because the whole expression will be false anyway.
How to Avoid Short-Circuiting
You can bypass this in Python by using the bitwise versions of the logic operators:
and -> &
or -> |
For example:
if True & bool(print("Hello1")):
pass
if False & bool(print("Hello2")):
pass # "Hello2" will be printed this time
Note however that you need to wrap print in bool because bitwise only works on the same data types.
Performance
I would imagine performance wise the one with only one if statement would be faster as other wise python might have to go through 2 if statements if it finds the conditions are true.
This is called short-circuiting, and it is mentioned in the docs.
And the answer is, Python will not evaluate b if a is False.
It evaluates from left to right, simple experiment
def iftrue1():
print("iftrue1")
return True
def iftrue2():
print("iftrue2")
return True
if iftrue1() and iftrue2():
pass
This outputs
iftrue1
iftrue2

Boolean Python Value confusion

I'm new to Python and while trying Python logical statements.I came across this which I'm not able to understand.Can anyone tell me whats happening here in Python 2.7.Whats the difference between 0 and False value in Python.
>>> 0 or False
False
>>> False or 0
0
Why the interpreter is giving different answers ?
You are being confused by the behaviour of the or operator; it returns the first expression that only if it is a true value; neither 0 nor False is true so the second value is returned:
>>> 0 or 'bar'
'bar'
>>> False or 'foo'
'foo'
Any value that is not numerical 0, an empty container, None or False is considered true (custom classes can alter that by implementing a __bool__ method (python 3), __nonzero__ (python 2) or __len__ (length 0 is empty).
The second expression is not even evaluated if the first is True:
>>> True or 1 / 0
True
The 1 / 0 expression would raise a ZeroDivision exception, but is not even evaluated by Python.
This is documented in the boolean operators documentation:
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.
Similarly, and returns the first expression if it is False, otherwise the second expression is returned.
The nature of this behavior is in python's order of expression evaluation. Python evaluates expressions from left to right, and it does it in a lazy manner. This means, that ones interpreter reaches the point, when the value of the expression is True, regardless of the rest of the expression, it will follow the branch of workflow, associated with the expression. If none of the expressions is True, it will simply return the most recent (last one). This gives the benefits of saving computational resources. Consider the following code:
>>>False or False or True or range(10**8)
True
>>>
Note, that range(10**8) is never called in this case, hence, a lot of time is saved.

in OR condition which side evaluate first in python?

if cpu_expensive_condition() or simple_condition():
do_something()
out of two condition in OR statement in above python code which will be evaluate first ? , and is it compulsory that both will be evaluate ?
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.
Quoted from Python Language Reference
Python evaluates from left to right, and both sides do not need to be evaluated. Consider the following example.
def left(x):
print 'left'
return x
def right(x):
print 'right'
return x
if left(False) or right(False):
print 'Done'
if left(True) or right(True):
print 'Done'
This will produce the following output:
left
right #This is where the first if statement ends.
left
Done #Only the left side was evaluated
According to Boolean Operations — and, or, not in the Python documentation:
This is a short-circuit operator, so it only evaluates the second argument if the first one is False.
So cpu_expensive_condition() will always be evaluated. simple_condition() will only be evaluated when cpu_expensive_condition() returns False (or something that evaluates to False, such as 0 or '' or None).
See also: Does Python support short-circuiting?
Python does short-circuit evaluation. Of course the first statement will be evaluated first. The second will only be evaluated if the first is False or false-ish.

Dive into python and-or fail

I am stuck at this particular example from dive into python
Example 4.18. When the and−or Trick Fails
>>>>a = ""
>>>>b = "second"
>>>1 and a or b
>>>>'second'
Since a is an empty string, which Python considers false in a boolean context, 1 and '' evalutes to '', and
then '' or 'second' evalutes to 'second'. Oops! That's not what you wanted.
The and−or trick, bool and a or b, will not work like the C expression bool ? a : b when a is false in a
boolean context.
Why does it says it isn't what the user wants, I mean 1 and "" would evaluate to False, while "" or b will evaluate to "second", that's perfectly what should happen, I don't understand why is it wrong?am I missing something?
No, you're not missing something.
The expr if cond else expr inline conditional was introduced to Python 2.5 in PEP 308; before that, conditionals had to be of the full form
if cond:
expr
else:
expr
However, people noticed that expr and cond or expr sort-of worked as an inline conditional, as long as the first expressions happened to evaluate to boolean true. As most things in Python are true, this syntax generally tended to work -- and then, of course, sometimes fail. This was in fact the motivation for adding the "proper" inline conditional.
The "and-or trick" was used to emulate ternary conditional operator, condition ? if-true : if-false or similar. Since 1 is equivalent to True in boolean context, the expression is expected to yield a, not b. So, the lesson is, don't use and-or trick. Python has ternary conditional since 2.5:
true if condition else false
Dive into python is not a good tutorial and should not be followed
The x and y or z expression was used before python had a proper ternary expression (y if x else z). This section is saying why it can surprise you if you expect it to behave like C's x ? y : z.
There's a list of tutorials on the python wiki. that might give you a more up to date resource.
Why does it says it isn't what the user wants, I mean 1 and "" would
evaluate to False, while "" or b will evaluate to "second", that's
perfectly what should happen, I don't understand why is it wrong?am I
missing something?
I feel this part of the question is being ignored. condition and if-true or if-false is a trick used in versions >2.5, as explained in other answers, to mimic other languages' condition ? if-true : if-false or later versions' if-true if condition else if-false.
The problem with it is that if if-true is an object that evaluates to false ("", [], (), False or 0), then this would fail, as expected when you look at the evaluation, but which might creep in as a bug if if-true is dynamically set and can evaluate to false.
The naively expected result of condition and a or b would have been the contents of a ("", empty string), as the condition (1) evaluates to true, but as 1 and a evaluates to false so you get the contents of b.
The solution to this problem is to either use the Python 2.5+ syntax (if-true if condition else if-false) or make sure that if-true is never false. One way to accomplish the second is to pack if-true and if-false in a tuple or list, as a nonempty iterable is always true:
>>> a = ""
>>> b = "second"
>>> condition = 1
>>> condition and a or b
'second'
>>> condition and [a] or [b]
['']
>>> (condition and [a] or [b])[0]
''
It's a kind of ugly solution, but it's there if you need to target old versions of Python.

Categories