Show which common values are truthy and which are falsy, including the ones people usually get wrong.
Python decides truthiness with one simple rule, and all of the surprising cases come from the same place. An empty container is falsy, but a container that holds falsy things is truthy.
cases = {
'0': 0,
'0.0': 0.0,
'empty string': '',
'empty list': [],
'empty tuple': (),
'empty dict': {},
'empty set': set(),
'empty range': range(0),
'None': None,
'False': False,
}
for label in cases:
value = cases[label]
print(label, '->', bool(value))
# These are the cases that people get wrong.
print(bool('0')) # True, it is a non-empty string
print(bool([0])) # True, the list holds a zero
print(bool([False])) # True
print(bool((None,))) # True, it is a one-element tuple
The first block prints False for every entry, because each of those values is empty, or zero, or None. The second block prints True for all four, and that is the part you should explain out loud. Truthiness is about emptiness and not about the contents. A list that holds a single zero is not empty, so it is truthy.
The mechanism underneath is worth stating, because it is how you implement the rule for your own classes. bool() looks for a __bool__ method first, and it uses the return value of that method. If there is no __bool__, it looks for __len__, and it treats a length of zero as False. If the object has neither method, then the object is truthy by default. That is why a custom class with a __len__ that returns 0 is falsy inside an if statement even though it has no __bool__ at all.
The practical consequence is that if value: is a test for emptiness and not a test for presence. If a function can legitimately return an empty list, an empty string, or a zero, then you need an explicit test such as if value is not None:, otherwise those legitimate results get treated as a failure.