False or None vs. None or False - python

In [20]: print None or False
-------> print(None or False)
False
In [21]: print False or None
-------> print(False or None)
None
This behaviour confuses me. Could someone explain to me why is this happening like this? I expected them to both behave the same.

The expression x or y evaluates to x if x is true, or y if x is false.
Note that "true" and "false" in the above sentence are talking about "truthiness", not the fixed values True and False. Something that is "true" makes an if statement succeed; something that's "false" makes it fail. "false" values include False, None, 0 and [] (an empty list).

The or operator returns the value of its first operand, if that value is true in the Pythonic boolean sense (aka its "truthiness"), otherwise it returns the value of its second operand, whatever it happens to be. See the subsection titled Boolean operations in the section on Expressions in the current online documentation.
In both your examples, the first operand is considered false, so the value of the second one becomes the result of evaluating the expression.

EXPLANATION
You must realize that True, False, and None are all singletons in Python, which means that there exist and can only exist one, single instance of a singleton object, hence the name singleton. Also, you can't modify a singleton object because its state is set in stone, if I may use that phrase.
Now, let me explain how those Python singletons are meant to be used.
Let's have a Python object named foo that has a value None, then if foo is not None is saying that foo has a value other than None. This works the same as saying if foo, which is basically if foo == True.
So, not None and True work the same way, as well as None and False.
>>> foo = not None
>>> bool(foo)
True
>>> foo = 5 # Giving an arbitrary value here
>>> bool(foo)
True
>>> foo = None
>>> bool(foo)
False
>>> foo = 5 # Giving an arbitrary value here
>>> bool(foo)
True
The critical thing to realize and be aware of when coding is that when comparing two objects, None needs is, but True and False need ==. Avoid using if foo == None, only use if foo is None. Also, avoid using if foo != None and only use if foo is not None.
WARNING
If you are using if foo or if not foo when the value of foo happens to be None, beware of potential bugs in your code. So, don't check for a potential None value in conditional statements this way. Be on the safe side by checking for it as explained above, i.e., if foo is None or if foo is not None. It is very important to follow best practices shared by Python experts.
REMINDER
True is a 1 and False is a 0.
In the old days of Python, we only had the integer 1 to represent a truthy value and we had the integer 0 to represent a falsy value. However, it is more understandable and human-friendly to say True instead of 1 and it is more understandable and human-friendly to say False instead of 0.
GOOD TO KNOW
The bool type (i.e., values True and False) are a subtype of the int type. So, if you use type hints and you annotate that a function/method returns either an int or a bool (i.e., -> int | bool or -> Union[int, bool]), then mypy (or any other static type checker) won't be able to correctly determine the return type of such a function/method. That's something you need to be aware of. There's no fix for this.

A closely related topic: Python's or and and short-circuit. In a logical or operation, if any argument is true, then the whole thing will be true and nothing else needs to be evaluated; Python promptly returns that "true" value. If it finishes and nothing was true, it returns the last argument it handled, which will be a "false" value.
and is the opposite, if it sees any false values, it will promptly exit with that "false" value, or if it gets through it all, returns the final "true" value.
>>> 1 or 2 # first value TRUE, second value doesn't matter
1
>>> 1 and 2 # first value TRUE, second value might matter
2
>>> 0 or 0.0 # first value FALSE, second value might matter
0.0
>>> 0 and 0.0 # first value FALSE, second value doesn't matter
0

From a boolean point of view they both behave the same, both return a value that evaluates to false.
or just "reuses" the values that it is given, returning the left one if that was true and the right one otherwise.

Condition1 or Condition2
if Condition1 is False then evalute and return Condition2.
None evalutes to False.

Related

How to check if all values in a dataframe are True

pd.DataFrame.all and pd.DataFrame.any convert to bool all values and than assert all identities with the keyword True. This is ok as long as we are fine with the fact that non-empty lists and strings evaluate to True. However let assume that this is not the case.
>>> pd.DataFrame([True, 'a']).all().item()
True # Wrong
A workaround is to assert equality with True, but a comparison to True does not sound pythonic.
>>> (pd.DataFrame([True, 'a']) == True).all().item()
False # Right
Question: can we assert for identity with True without using == True
First of all, I do not advise this. Please do not use mixed dtypes inside your dataframe columns - that defeats the purpose of dataframes and they are no more useful than lists and no more performant than loops.
Now, addressing your actual question, spolier alert, you can't get over the ==. But you can hide it using the eq function. You may use
df.eq(True).all()
Or,
df.where(df.eq(True), False).all()
Note that
df.where(df.eq(True), False)
0
0 True
1 False
Which you may find useful if you want to convert non-"True" values to False for any other reason.
I would actually use
(pd.DataFrame([True, 'a']) == True).all().item()
This way, you're checking for the value of the object, not just checking the "truthy-ness" of it.
This seems perfectly pythonic to me because you're explicitly checking for the value of the object, not just whether or not it's a truthy value.

Understanding the truthiness of strings

I understand that Python built-in types have a "truthiness" value, and the empty string is considered False, while any non-empty string is considered True.
This makes sense
I can check this using the built-in function bool.
>>> bool("")
False
>>> bool("dog")
True
I can also make use of these truthiness values when using conditionals. For example:
>>> if "dog":
... print("yes")
...
yes
This is confusing
This doesn't work with the == operator though:
>>> "dog" == True
False
>>> "dog" == False
False
Can anyone explain why == seems to act differently than a conditional?
See the truth value testing and comparisons sections of the documentation, excerpted below.
In a nutshell, most things are truthy by default, which is why bool("dog") is true. The == operator compares two objects for equality, as opposed to comparing their truthinesses, as I assume you had expected.
4.1. Truth Value Testing
Any object can be tested for truth value, for use in an if or while condition or as operand of the Boolean operations below.
By default, an object is considered true unless its class defines
either a __bool__() method that returns False or a __len__() method
that returns zero, when called with the object.
Here are most of the built-in objects considered false:
constants defined to be false: None and False
zero of any numeric type: 0, 0.0, 0j, Decimal(0), Fraction(0, 1)
empty sequences and collections: '', (), [], {}, set(), range(0)
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.)
4.3. Comparisons
Objects of different types, except different numeric types, never
compare equal.
...
Non-identical instances of a class normally compare as non-equal
unless the class defines the __eq__() method.
The basics
I believe your confusion might come from comparing Python to languages such as JavaScript where there is a == and a === operator. Python does not work this way.
In Python the only way to compare for equality is with == and this compares both value and type.
Thus if you compare True == "dog", then the expression is immediately False because the types bool and str are not types that can be compared.
Although, note that it does not mean that there are no types that are comparable between themselves. Examples are set and frozenset:
frozenset({1,2,3}) == {1,2,3} # True
Or simply int and float
1 == 1.0 # True
This is the behaviour for most built-in types.
The classy part
In the case where you define your own types, i.e. when you define classes, you can write the __eq__ which is called when you compare a class object to another value.
By example you could do this (which by the way was pointed out as a terrible idea in the comments, you should not inherit built-in types).
class WeirdString(str):
def __eq__(self, other):
return str(self) == str(other) or bool(self) == bool(other)
s = WeirdString("dog")
s == True # True
In the case where you do not define __eq__, then Python fall back on comparing whether the objects are the same object with is.
When you compare "dog" == True, you are also comparing the type of these objects and not just their boolean value.
Now as True has a type bool and "dog" has a type str, they are not equivalent according to the == operator, irrespective of their boolean values being equal.
Note: Both the object's type,boolean values are being checked here.

Difference between if and if not python

Can anybody please tell me here what is the exact difference between if and if not here in the code.
def postordertraversse(self,top):
m=[]
if(top):
if not self.postordertraversse(top.left):
m.append(top.root)
top_most=m.pop(0)
conv=createlist();
conv.postordertraversse(conv.top)
What i can understand is if top means if top object instance exists then move inside the block and check if not i.e till top.left is not null keep appending.
if x: means "if x is truthy".
if not x: means "if x is falsey".
Whether something is truthy or falsey depends on what kind of object it is.
For numbers, 0 is falsey, and all other values are truthy.
For boolean values, True is truthy and False is falsey (obviously!)
For collections (lists, tuples, dictionaries, strings, etc), empty ones are falsey and non-empty ones are truthy.
So in your example code, the two if statements are saying:
if top is truthy:
if the result of self.postordertraversse(top.left) is falsey:
not in python is like negation in other programming languages.
'not' statement just converts further expression. For example:
not True - False
not False - True

Python `None` passed into type-conversion functions

What is the rationale for the design decision for None type being passed into type-conversion functions?
bool(None) returns False - which makes perfect sense
str(None) returns 'None' which is not okay - Returning an empty string would be a better choice as below
>>> bool(None)
False
>>> bool(str(None)) #Returning empty string will make it consistent by returning False
True
>>> bool('')
False
And list(None), dict(None), tuple(None), int(None), float(None) return Type errors - From which if they return [], {}, (), 0 and 0.0 it would have been a obvious and will be useful in many real life scenarios to avoid manual handling.
It's appreciated If someone could explain what might have motivated Guido to go the other way around.
PS : This arose from this question Python: most idiomatic way to convert None to empty string?. Most of the answers are of if-else approaches where as in real life converting/overriding None by empty string, list, dict or 0 would be common.
None is still an object. It is not an empty value, it is merely a sentinel to use anywhere you have to produce or use an object but you really wanted to leave it empty.
Function calls don't require an argument, calling something with no arguments is perfectly valid. As such there is no point in passing in None, as that would be one argument, not zero.
To create 'empty' objects using the type functions then, just leave the argument list empty:
>>> list()
[]
>>> tuple()
()
>>> str()
''
bool() gives you the boolean type of the argument you passed in. You can call it without an argument:
>>> bool()
False
but if you pass in None, it'll determine the truth value of that object. For None that value is False. str(None) produces the string 'None', which is not empty so it is True when passed to bool(). See the Truth Value Testing section.

What should replace comparisons with False in python?

I have a function that returns False if something isn't found, and returns a data structure with what I'm looking for otherwise. When I assign the value returned from this function to a variable my_var in a for loop, I do a if my_var == False: continue to carry on.
pychecker doesn't like this and reports Comparisons with False are not necessary and may not work as expected.
What's the python way for doing this?
As a return value indicating absence of a result, None is almost always better.
If you must use False, use not my_var. This assumes non - False return values are never "falsy" (bool() converts it into False - this is the case for empty strings, empty collections, and a few other things).
If objects may be falsy but must be distinguished from False, then the comparison is necessary and you'll have to ignore the error (a minor variation, my_var is False would be slightly more idiomatic, but it's virtually the same). But that's not a nice situation to be in and you should avoid it for the fake of clarity.
To repeat myself: If at all possible, use None and test for my_var is None.
Normally you would return None instead of False i.e. if an object doesn't match. And then check if my_var == None: as all not 0, not False and not None return True.
If you just want to check for a falsy value:
myval = findit('something')
if not myval:
"not found"
You don't want to do this if you could actually return a value for the "is-found" case which has a falsy value. E.g., if [] (empty list) is found, this will still report "not found".
If you care that it is actually False, use is. True, False, and None are singletons, so the identity comparison operator is used.
if myval is False:
"not found"
In this case you are using False as a sentinel value to indicate that there is no result.
Usually None is used as a sentinel "no-value", however, so I suggest you change the signature of your function to return None if no value is found. This conforms better with Python idiom and the behavior of methods such as {}.get('missing-key').
myval = findit('something')
if myval is None:
"not found"
In cases where no sentinel value is possible because any value could be returned (even None or False), Python uses an exception to signal that no value was found. E.g. {}['missing-key'] will raise KeyError.
Usually you'd just use not:
if not my_var: continue
Python is actually more strongly typed than C++ in this regard. Only genuine booleans can be True or False.
So my_var must be a boolean. Therefore you just check it as one.
if not my_var:
continue

Categories

Resources