if a or b explanation - python

a = None
b = 'Test'
if a is None or b is None:
print('Test')
this version is obviously working but:
if (a or b) is None:
print('Test')
Expected would be the same result but it evaluates to False, why?

In Python, an or statement does not just return True or False, it returns the first of it's arguments that is "truthy".
In your code, the following happens when evaluating (a or b) is None:
None is falsy, and a is None, so a or b returns b.
b is not None.
(a or b) is None resolves to False.

Operator precedence and evaluation rules of boolean expressions work a bit different in Python. This is not doing what you imagine:
(a or b) is None
The above condition is asking whether the result of evaluating (a or b) is None, and it will never be None with the current values of a and b; a or b will return 'Test' (the value of b), because a is None. If you want to test if either one of two values is None, only the first syntax is valid:
a is None or b is None
Just to be clear, in Python x or y will return the value of the first non-false expression, and None, [], {}, '', 0 and False are considered false. If what you want is to shorten the expression a bit, this is equivalent to the first version of your code:
if not a or not b:

If you want to be super brief, you can used the following construction:
if None in (a, b):
print('Test')
Please note that while this will work in the majority of cases, the expression is not exactly equivalent to a is None or b is None but rather a == None or b == None. See explanation of why this is not the same thing.

Related

Python: `and` operator does not return a boolean value

In Python, an empty list is considered a Falsey value
Therefore this is how things should work:
>>> [] and False
False
But in reality, python returns an empty list.
>>> [] and False
[]
Is this intended or a bug?
It's intended. Both and and or are defined to return the last thing evaluated (based on short-circuiting), not actually True or False. For and, this means it returns the first falsy value, if any, and the last value (regardless of truthiness) if all the others are truthy.
It was especially useful back before the conditional expression was added, as it let you do some almost-equivalent hacks, e.g. before the conditional expression:
b if a else c
could be written as:
a and b or c
and, assuming b itself was some truthy thing, it would behave equivalently (the conditional expression lacked that limitation and was more clear about intent, which is why it was added). Even today this feature is occasionally useful for replacing all falsy values with some more specifically-typed default, e.g. when lst might be passed as None or a list, you can ensure it's a list with:
lst = lst or []
to cheaply replace None (and any other falsy thing) with a new empty list.
This is how it is supposed to work. and will only return the right hand operand if the left hand operand is truthy. Since [] is falsy, and returns the left hand operand.
That's a totally expected behaviour. To understand it, you need to know how the Boolean operators (and, or, not) work. From 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.
Now let's consider your example: [] and False. Here, since [] is falsey, it's value is returned back by the statement which is [].
Above linked Python documentation explicitly mentions:
Note: Neither and nor or restrict the value and type they return to False and True, but rather return the last evaluated argument.
However, in case you need the return value as boolean, you can explicitly type-cast the value to True or False using the bool() function.
For example, in your case it will return as False:
>>> bool([] and False)
False

Using ternary if-else in python expect with and-or keywords

I've recently seen on this website that you can use the syntax
var = cond and value1 or value2
Instead of the conventional ternary syntax
var = value1 if cond else value2
I've since failed to find the user that said it or any articles online related to this topic, but after trying it in my code it seems to work as expected. Can someone please clarify if this syntax is actually valid or if this is just a fluke?
It doesn't always work. For example,
True and False or None
returns None, while
False if True else None
returns False.
I recommend reading the language spec, on boolean operators, which says:
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 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.
is this a fluke?
No. As you can see, it works. It works because the return value of
a and b is b if True, or False.
When to use
Now that we have the ternary, x = a and b or c is as long or short as x = b if a else c, and, I claim, harder to read. But this is shorter:
def x(mut = None):
mut = mut or {}
and simpler than the horrible:
def x(mut = None):
mut = mut if mut else {}
and still cleaner than:
def x(mut=None):
if not mut:
mut = {}
Which puts a couple of short indented lines at the beginning of a function just where my eye doesn't want them.
Note that in this case we are using the (parallel) fact that or returns the first true element or False.

and operation in python

Following is the code that is yielding confusing results.
a = None
b = 1
print a and b
if a and b is None:
print "True"
else:
print "False"
Here bool(a) is false as it's none. So via short circuit, the expression should return None. This is actually happening. However, during if condition, the condition fails. Although a and b yields None, the condition fails and priting false at the output. Can someone explain why it's happening?
a and b is None is evaluated the same as a and (b is None), so the expression evaluates to None, and execution jumps to the else clause.
If you add brackets (a and b) is None, then your code will print "True" as you expect.
This is because is has a higher precedence than and in Python. Take a look at the operator precedence documentation for more details.

Python is, == operator precedence

In Python3,
a = b = 3
a is None == b is None
returns False, but
(a is None) == (b is None)
returns True. So I would assume based on this example alone, == has precedence over is.
However,
a = b = None
a is None == b is None
returns True. And
(a is None) == (b is None)
returns True. But
a is (None == b) is None
returns False. In this case, it would seem as if is has precedence over ==.
To give another example, and this expression isn't meant to do anything, but bear with me please. If I say
None is None == None
it returns True. But both of the following return False.
None is (None == None)
(None is None) == None
So clearly, Python isn't evaluating these with some strict precedence, but I'm confused what is going on. How is it evaluating this expression with 2 different operators, but differently from either order?
What you see here is operator chaining and there is no precedence involved at all!
Python supports expressions like
1 < a < 3
To test that a number is in between 1 and 3; it's equal to (1 < a) and (a < 3) except that a is only evaluated once.
Unfortunately that also means that e.g.
None is None == None
actually means
(None is None) and (None == None)
which is of course True, and the longer example you started with
a = b = 3
a is None == b is None
means
(a is None) and (None == b) and (b is None)
which can only be True if both a and b are None.
Documentation here, see the bit about chaining.
Very useful sometimes but it also pops up when you least expect it!
According to the documentation, all python comparisons operators has same priority:
There are eight comparison operations in Python. They all have the
same priority (which is higher than that of the Boolean operations).
However by wrapping comparisons with the brackets, they start to be the atoms expressions, so statements in brackets evaluated before statements outside, that impacts the order of the evalutation, I will decompose the first "contradictional" case, the all others is similar:
a = b = 3
a is None == b is None
Per documentation the priority is the same, so evaluation is next:
1. a is None ? -> False # Because a == 3
2. False == b -> False # Because b == 3
3. False is None
Please see order for the second case below:
(a is None) == (b is None)
1. a is None ? -> False # Because a == 3
2. b is None -> False # Because b == 3
3. False is False -> True

Why do I get 0 when I run "0 and True" or "0 and False"

consider this code:
>>> 0 and True
0
>>> 0 and False
0
Why do I get 0 when I run the above commands in Python?
When the first condition of an and evaluates to False (which 0 does in Python), the second argument is not evaluated at all, because the and will never become true. This is called short-circuiting. In that case, the result of the expression is the first operand, in your case 0.
Because 0 is a false value (all numeric 0 values are, as well as empty containers, and None and False).
The and operator short-curcuits; if the left-hand expression evaluates to a false value, it is returned, otherwise the right-hand expression outcome is returned.
The or operator does the same, but for a true left-hand value; 1 or False returns 1.
From 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.
You can make handy use of this:
foo = None
if something_or_other:
foo = lambda arg: arg * 3
outcome = foo and foo('bar')
outcome = foo or expensive_call()
where foo is only called if it is actually defined and not still None; the expensive_call() is only invoked if foo is not yet bound to a true value.
You are ANDing with zero. You should get zero always.
this is because, logical operators are evaluated from left to right. So, for or if True find rest of the expression not evaluated:
>>> True or "one"
True
>>> False or "one"
'one'
Similarly for and, if False found rest of the expression ignored:
>>> True and "one"
'one'
>>> False and "one"
False
In compiler design, this concept is called short-circuiting and this is same design for most of compilers.
most of programming languages have this feature, it's fast branch detection, in your case and if first condition is false, evaluation is fail and second (other) condition never check
in other case or if first condition return true second (other) condition never check and result will be true
it's really cool feature look this sample for example:
if( list != null and !list.isEmpty() ) {
// do stuff
}
if this feature does not exists this if statement cause exception, but now !list.isEmpty() will never runs when list is null

Categories

Resources