Use of True, False, and None as return values in python functions


Answers

Use if foo or if not foo, there is no need for either == or is for that.

For checking against None, is None and is not None are recommended. This allows you to distinguish it from False (or things that evaluate to False, like "" and []).

Whether get_attr should return None would depend on the context. You might have an attribute where the value is None, and you wouldn't be able to do that. I would interpret None as meaning "unset", and a KeyError would mean the key does not exist in the file.

Question

I think that I fully understand this, but I just want to make sure since I keep seeing people say to NEVER EVER test against True, False, or None. They suggest that routines should raise an error rather than return False or None. Anyway, I have many situations where I simply want to know if a flag is set or not so my function returns True or False. There are other situations where I have a function return None if there was no useful result. From my thinking, neither is problematic so long as I realize that I should never use:

if foo == True
if foo == False
if foo == None

and should instead use:

if foo is True
if foo is False
if foo is None

since True, False, and None are all singletons and will always evaluate the way I expect when using "is" rather than "==". Am I wrong here?

Along the same lines, would it be more pythonic to modify the functions that sometimes return None so that they raise an error instead? Say I have an instance method called "get_attr()" that retrieves an attribute from some file. In the case where it finds that the attribute I requested does not exist is it appropriate to return None? Would it be better to have them raise an error and catch it later?




You can surround the subprocess invocation by a try-except block:

try:
    data = subprocess.check_output(func, shell=True)
except Exception:
    data = None

Also, writing if data: would be better than if data is True:.




Given 2 int values, return True if one is negative and other is positive

All comparison operators in Python have the same precedence. In addition, Python does chained comparisons. Thus,

(a < 0 != b < 0)

breaks down as:

(a < 0) and (0 != b) and (b < 0)

If any one of these is false, the total result of the expression will be False.

What you want to do is evaluate each condition separately, like so:

(a < 0) != (b < 0)

Other variants, from comments:

(a < 0) is not (b < 0) # True and False are singletons so identity-comparison works

(a < 0) ^ (b < 0) # bitwise-xor does too, as long as both sides are boolean

(a ^ b < 0) # or you could directly bitwise-xor the integers; 
            # the sign bit will only be set if your condition holds
            # this one fails when you mix ints and floats though

(a * b < 0) # perhaps most straightforward, just multiply them and check the sign



Comparisons with boolean values in Python

You shouldn't be using == True or == False either, unless you explicitly need to test against the boolean value (which you almost never don't). The preferred way to perform boolean tests in Python is if foo and if not foo.

However, from a technical point of view, there's nothing wrong with using is True and is False. In PEP 285 (which defined the bool type):

The values False and True will be singletons, like None. Because the type has two values, perhaps these should be called "doubletons"? The real implementation will not allow other instances of bool to be created.

So whenever you use True or False you end up with the same instance.




Concerning whether to raise an exception or return None: it depends on the use case. Either can be pythonic. Look at python's dict class for example, x[y] hooks into dict.__getitem__ and it raises a KeyError if key is not present. But dict.get method returns the second argument (which is defaulted to None) if key is not present. They are both useful.

The most important thing to consider is to document that behaviour in the docstring, and make sure that your get_attr() method does what it says it does.

To address your other questions, use these conventions:

if foo:
    # for testing truthiness

if not foo:
    # for testing falsiness

if foo is None:
    # testing .. Noneliness ?

if foo is not None:
    # check explicitly avoids common bugs caused by empty sequences being false

Functions that return True or False should probably have a name that makes this obvious to improve code readability

def is_running_on_windows():
    return os.name == 'nt'

In python3 you can "type-hint" that:

>>> def is_running_on_windows() -> bool:
...     return os.name == 'nt'
... 
>>> is_running_on_windows.__annotations__
{'return': bool}



You can use this

return (a < 0) != (b < 0)

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).

So it becomes

(a < 0) and (0 != b) and (b < 0)

See https://docs.python.org/3/reference/expressions.html#not-in