What should replace comparisons with False in python? - 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

Related

What's the difference between If not (variable) and if (variable) == false?

I'm learning Python and I just started learning conditionals with booleans
I am very confused though as to the specific topic of "If Not". Could someone please explain to me the difference between :
x = False
if not x:
print("hello")
if x == False:
print("hello")
When testing this code on a Python compiler, I receive "hello" twice. I can assume this means that they both mean the same thing to the computer.
Could someone please explain to me why one would use one method over the other method?
It depends™. Python doesn't know what any of its operators should do. It calls magic methods on objects and lets them decide. We can see this with a simple test
class Foo:
"""Demonstrates the difference between a boolean and equality test
by overriding the operations that implement them."""
def __bool__(self):
print("bool")
return True
def __eq__(self, other):
print("eq", repr(other))
return True
x = Foo()
print("This is a boolean operation without an additional parameter")
if not x:
print("one")
print("This is an equality operation with a parameter")
if x == False:
print("two")
Produces
This is a boolean operation without an additional parameter
bool
This is an equality operation with a parameter
eq False
two
In the first case, python did a boolean test by calling __bool__, and in the second, an equality test by calling __eq__. What this means depends on the class. Its usually obvious but things like pandas may decide to get tricky.
Usually not x is faster than x == False because the __eq__ operator will typically do a second boolean comparison before it knows for sure. In your case, when x = False you are dealing with a builtin class written in C and its two operations will be similar. But still, the x == False comparison needs to do a type check against the other side, so it will be a bit slower.
There are already several good answers here, but none discuss the general concept of "truthy" and "falsy" expressions in Python.
In Python, truthy expressions are expression that return True when converted to bool, and falsy expressions are expressions that return False when converted to bool. (Ref: Trey Hunner's regular expression tutorial; I'm not affiliated with Hunner, I just love his tutorials.)
Truthy stuff:
What's important here is that 0, 0.0, [], None and False are all falsy.
When used in an if statement, they will fail the test, and they will pass the test in an if not statement.
Falsy stuff:
Non-zero numbers, non-empty lists, many objects (but read #tdelaney's answer for more details here), and True are all truthy, so they pass if and fail if not tests.
Equality tests
When you use equality tests, you're not asking about the truthiness of an expression, you're asking whether it is equal to the other thing you provide, which is much more restrictive than general truthiness or falsiness.
EDIT: Additional references
Here are more references on "Truthy" and "Falsy" values in Python:
Truth value testing in the Python manual
The exhaustive list of Falsy values
Truthy and Falsy tutorial from freeCodeCamp
In one case you are checking for equality with the value "False", on the other you are performing a boolean test on a variable. In Python, several variables pass the "if not x" test but only x = False passes "if x == False".
See example code:
x = [] # an empty list
if not x: print("Here!")
# x passes this test
if x == False: print("There!")
# x doesn't pass this test
Try it with x = None: not x would be True then and x == False would be False. Unlike with x = False when both of these are True. not statement also accounts for an empty value.

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.

Python None comparison: should I use "is" or ==?

My editor warns me when I compare my_var == None, but no warning when I use my_var is None.
I did a test in the Python shell and determined both are valid syntax, but my editor seems to be saying that my_var is None is preferred.
Is this the case, and if so, why?
Summary:
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?).
Explanation:
You can have custom classes where my_var == None will return True
e.g:
class Negator(object):
def __eq__(self,other):
return not other
thing = Negator()
print thing == None #True
print thing is None #False
is checks for object identity. There is only 1 object None, so when you do my_var is None, you're checking whether they actually are the same object (not just equivalent objects)
In other words, == is a check for equivalence (which is defined from object to object) whereas is checks for object identity:
lst = [1,2,3]
lst == lst[:] # This is True since the lists are "equivalent"
lst is lst[:] # This is False since they're actually different objects
is is generally preferred when comparing arbitrary objects to singletons like None because it is faster and more predictable. is always compares by object identity, whereas what == will do depends on the exact type of the operands and even on their ordering.
This recommendation is supported by PEP 8, which explicitly states that "comparisons to singletons like None should always be done with is or is not, never the equality operators."
PEP 8 defines that it is better to use the is operator when comparing singletons.
I recently encountered where this can go wrong.
import numpy as np
nparray = np.arange(4)
# Works
def foo_is(x=None):
if x is not None:
print(x[1])
foo_is()
foo_is(nparray)
# Code below raises
# ValueError: The truth value of an array with more than one element is ambiguous. Use a.any() or a.all()
def foo_eq(x=None):
if x != None:
print(x[1])
foo_eq()
foo_eq(nparray)
I created a function that optionally takes a numpy array as argument and changes if it is included. If I test for its inclusion using inequality operators !=, this raises a ValueError (see code above). If I use is not none, the code works correctly.
A useful tidbit to add to people's understanding.
The reason that we check for identity with None is because Python only ever stores the value None in one place in memory, and every object which equals None has its value stored in this same location. There are a handful of "special values" which get this treatment, and None is just one of them.
But most values do not get this special treatment! For example, the float 1.25 can be stored in different locations in memory:
a = None
b = None
a is b
True
a = 1.25
b = 1.25
a is b
False
It just so happens that None is among the handful of values which are always stored in one place in memory. Another example is any integer between -5 and 256... since these integers are used often, they are always stored in memory, and every integer with that value is stored in the same place in your computer's memory! Try it out:
a = 256
b = 256
a is b
True
a = 257
b = 257
a is b
False
So you can think of None as being part of a special class of values which always have a constant memory address. That is why we can use is to check whether two variables are both None... it just checks whether the memory address is the same.
Edit: Joooeey makes the good point that which integers are stored in memory is specific to your python implementation, and the example of numbers from -5 to 256 is specific to CPython. If you don't know what you're running, it's probably CPython, which is the most common implementation. But for this reason (and others) it is better practice to compare equality between these numbers with a == 2 and not with a is 2. As for None, it is specified to be the sole instance of the NoneType type according to the Python Documentation itself, so regardless of implementation you can always compare it using a is None.
Another instance where "==" differs from "is". When you pull information from a database and check if a value exists, the result will be either a value or None.
Look at the if and else below. Only "is" works when the database returns "None". If you put == instead, the if statement won't work, it will go straight to else, even though the result is "None". Hopefully, I am making myself clear.
conn = sqlite3.connect('test.db')
c = conn.cursor()
row = itemID_box.get()
# pull data to be logged so that the deletion is recorded
query = "SELECT itemID, item, description FROM items WHERE itemID LIKE '%" + row + "%'"
c.execute(query)
result = c.fetchone()
if result is None:
# log the deletion in the app.log file
logging = logger('Error')
logging.info(f'The deletion of {row} failed.')
messagebox.showwarning("Warning", "The record number is invalid")
else:
# execute the deletion
c.execute("DELETE from items WHERE itemID = " + row)
itemID_box.delete(0, tk.END)
messagebox.showinfo("Warning", "The record has been deleted")
conn.commit()
conn.close()

Python: testing for None, testing for boolean value

Is there any low-level, implementation-related difference (performance-ish) between these approaches..?
# check if string is empty
# the preferred way it seems [1]
if string:
print string
else:
print "It's empty."
# versus [2]
if string is '':
# or [3]
if string == '':
For example, when testing for None, I still find it more readable and explicit to do:
if some_var is not None:
..instead of..
if not some_var:
if not some_var, at least for me, always reads "if some_var does not exist".
Which is better to use, what are the proper use cases for ==, is and bool-testing?
Never use is for (value) equality testing. Only use it to test for object identity. It may work for the example if string is '', but this is implementation dependent, and you can't rely on it.
>>> a = "hi"
>>> a is "hi"
True
>>> a = "hi there!"
>>> a is "hi there!"
False
Other than that, use whatever conveys the meaning of your code best.
I prefer the shorter if string:, but if string != '': may be more explicit.
Then again if variable: works on every kind of object, so if variable isn't confined to one type, this is better than if variable != "" and variable != 0: etc.
To expand on Tim Pietzcker's answer:
if string:
print string
This tests if string evaluates to True. I.E.
>>> bool("")
False
>>> bool(None)
False
>>> bool("test")
True
So it's not only testing if it is empty, but if it is None or empty. This could have an impact depending on how you treat None/empty.
Firstly, don't use if string is '': because this is not guaranteed to work. The fact that CPython interns short strings is an implementation detail and should not be relied on.
Using if string: to check that string is non-empty is, I think, a Pythonic way to do it.
But there is nothing wrong about using if string == ''.

False or None vs. None or False

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.

Categories

Resources