Skip to content
VLSI Mentor

Python Part I · Module 9 · Conditional Statements

What Counts as True: Truthiness

Chapter 9.1 slipped something past you. The verdict chain was written if fatals: rather than if fatals > 0:, and it worked — an integer sat where a comparison was expected and Python chose a branch from it without complaint. That is not a shortcut or a special case for numbers. Every value in Python can be used as a condition, and every value is treated as either true or false when it is. The rule behind that is short, designed, and worth understanding properly rather than memorising, because it produces the single most readable idiom in the language and also one genuinely dangerous ambiguity.

Foundation15 min readPythonPart IFundamentalsConditionalsTruthiness

Part I · Module 9 · Chapter 9.5 · What Counts as True: Truthiness

1. A Condition Need Not Be a Comparison

Every condition so far produced True or False by comparing something. This one does not:

Azvya Education Pvt. Ltd.VLSI Mentor
Snippet
errors = 4

if errors:
    print("there were errors")
Azvya Education Pvt. Ltd.VLSI Mentor
Snippet
there were errors
Azvya Education Pvt. Ltd.VLSI Mentor
Snippet
errors = 0

if errors:
    print("there were errors")
else:
    print("no errors")
Azvya Education Pvt. Ltd.VLSI Mentor
Snippet
no errors

errors is an integer, not a Boolean. Python did not refuse it and did not convert the statement into a comparison. It asked a different question: does this value count as true?

That question has an answer for every value in the language, and bool() reports it directly:

Azvya Education Pvt. Ltd.VLSI Mentor
Snippet
print(bool(4))
print(bool(0))
Azvya Education Pvt. Ltd.VLSI Mentor
Snippet
True
False

Chapter 9.1's model still holds exactly — evaluate the expression, get a truth value, select a path. What is new is that the truth value can come from interpreting an ordinary value rather than from a comparison producing a bool.

2. The Values That Count as False

The list is short. These are the ones you will meet:

Azvya Education Pvt. Ltd.VLSI Mentor
Snippet
print(bool(0))
print(bool(0.0))
print(bool(""))
print(bool([]))
print(bool(()))
print(bool({}))
print(bool(set()))
print(bool(None))
print(bool(False))
Azvya Education Pvt. Ltd.VLSI Mentor
Snippet
False
False
False
False
False
False
False
False
False
ValueTypeWhy it is false
0intzero
0.0floatzero
""strempty
[]listempty
()tupleempty
{}dictempty
set()setempty
NoneNoneTypeabsent
Falseboolfalse

Everything else you have met counts as true.

3. The Values That Look False and Are Not

This is where the memorised version of the rule fails people:

Azvya Education Pvt. Ltd.VLSI Mentor
Snippet
print(bool("0"))
print(bool("False"))
print(bool(" "))
print(bool([0]))
print(bool([[]]))
print(bool(-1))
Azvya Education Pvt. Ltd.VLSI Mentor
Snippet
True
True
True
True
True
True

Every one of those is true.

  • "0" is a string with one character in it. It is not empty, so it is true. The character happens to be a zero, which Python is not interpreting.
  • "False" is a string with five characters. Same reasoning. This is the classic trap when reading a configuration value from a file: if value: is true for the text "False".
  • " " is a string containing a space — not empty.
  • [0] is a list with one item. The list is not empty, so it is true, regardless of the item being falsy.
  • [[]] is a list containing one empty list. The outer list has one item.
  • -1 is non-zero.

4. The Rule Behind the List

The table is not a set of special cases to memorise. One idea generates almost all of it:

A value counts as false when it represents nothing — zero, empty, or absent. Otherwise it counts as true.

Zero is the nothing of numbers. The empty string, list, tuple, dict and set are the nothing of their containers. None is the explicit absence of a value. Read the table again with that sentence in hand and it stops being arbitrary.

The practical consequence is that a condition can ask the natural question directly:

Azvya Education Pvt. Ltd.VLSI Mentor
Snippet
failed = []

if failed:
    print("some tests failed")
else:
    print("no failures recorded")
Azvya Education Pvt. Ltd.VLSI Mentor
Snippet
no failures recorded
Azvya Education Pvt. Ltd.VLSI Mentor
Snippet
failed = ["reset", "crc"]

if failed:
    print("some tests failed")
else:
    print("no failures recorded")
Azvya Education Pvt. Ltd.VLSI Mentor
Snippet
some tests failed

if failed: reads as if there are any failures, which is exactly the question. The alternative, if len(failed) > 0:, computes a count and compares it to zero in order to ask whether a list has anything in it.

5. if errors: or if errors != 0:

Both work, and for a plain integer they select the same branch for every input. The choice is about which sentence the reader gets.

WrittenReads asBest when
if errors:if there were any errorsthe value is a count or a collection, and zero/empty means nothing happened
if errors != 0:if the error count is not zerozero is a meaningful value you are deliberately excluding
if failed:if anything failedalmost always, for collections
if len(failed) > 0:if the length is above zerorarely — it says how to check, not what is being asked

The honest guidance is not shorter is better. It is that the short form is better when the falsy case genuinely means "nothing". For a count of errors, a coverage list, a log line — it does, and if errors: is the clearer sentence.

6. When Zero Means Something

Here is the limit, and it is sharper than a style preference.

A dictionary records how many errors each test reported. One test ran and was clean. Another never ran at all.

Azvya Education Pvt. Ltd.VLSI Mentor
Snippet
counts = {"smoke": 0}

print(counts.get("smoke"))
print(counts.get("reset"))
Azvya Education Pvt. Ltd.VLSI Mentor
Snippet
0
None

Those are opposite facts: "smoke" has a recorded result of zero errors, and "reset" has no recorded result at all. Now ask about each with a truthiness condition:

Azvya Education Pvt. Ltd.VLSI Mentor
BROKEN ON PURPOSE
counts = {"smoke": 0}

if counts.get("smoke"):
    print("smoke has errors")
else:
    print("smoke: branch not taken")

if counts.get("reset"):
    print("reset has errors")
else:
    print("reset: branch not taken")
Azvya Education Pvt. Ltd.VLSI Mentor
Snippet
smoke: branch not taken
reset: branch not taken

Identical treatment. 0 is falsy and None is falsy, so a clean run and a missing run land in the same branch — and in a regression report, "passed cleanly" and "never ran" are the two things you most need to tell apart.

The separation requires asking the right question:

Azvya Education Pvt. Ltd.VLSI Mentor
Snippet
counts = {"smoke": 0}

print(counts.get("smoke") is None)
print(counts.get("reset") is None)
Azvya Education Pvt. Ltd.VLSI Mentor
Snippet
False
True

So the three-case version says what it means:

Azvya Education Pvt. Ltd.VLSI Mentor
Snippet
counts = {"smoke": 0}

if counts.get("reset") is None:
    print("reset has no recorded result")
elif counts["reset"] == 0:
    print("reset was clean")
else:
    print("reset reported errors")
Azvya Education Pvt. Ltd.VLSI Mentor
Snippet
reset has no recorded result

The same trap appears with positions, because 0 is a valid index:

Azvya Education Pvt. Ltd.VLSI Mentor
BROKEN ON PURPOSE
position = 0

if position:
    print("found at", position)
else:
    print("treated as not found")
Azvya Education Pvt. Ltd.VLSI Mentor
Snippet
treated as not found

The item was found, at the very first position, and the program reported it as not found.

7. if ready == True: Is Not the Same Question

A Boolean variable needs no comparison at all:

Azvya Education Pvt. Ltd.VLSI Mentor
Snippet
ready = True

if ready:
    print("ready")
Azvya Education Pvt. Ltd.VLSI Mentor
Snippet
ready

if ready == True: would work here, and it is redundant — ready is already the truth value, so comparing it to True produces the truth value again.

But the redundancy is not the real argument. The comparison and the truth test are different questions, and they disagree the moment the value is not a Boolean:

Azvya Education Pvt. Ltd.VLSI Mentor
Snippet
count = 3

print(bool(count))
print(count == True)
Azvya Education Pvt. Ltd.VLSI Mentor
Snippet
True
False

if count: is true. if count == True: is false, because 3 is not equal to True — although 1 is:

Azvya Education Pvt. Ltd.VLSI Mentor
Snippet
print(1 == True)
print(0 == False)
Azvya Education Pvt. Ltd.VLSI Mentor
Snippet
True
True

So == True silently narrows a truthiness test into an equality test that passes only for True and 1. Write if ready: for a Boolean, and an explicit comparison for anything else.

8. Blank Lines, the Classic Use

The idiom that appears in every log-processing script in existence:

Azvya Education Pvt. Ltd.VLSI Mentor
Snippet
line = "   "

if not line.strip():
    print("blank line")
else:
    print("has content")
Azvya Education Pvt. Ltd.VLSI Mentor
Snippet
blank line
Azvya Education Pvt. Ltd.VLSI Mentor
Snippet
line = "  UVM_ERROR  "

if not line.strip():
    print("blank line")
else:
    print("has content:", line.strip())
Azvya Education Pvt. Ltd.VLSI Mentor
Snippet
has content: UVM_ERROR

Three ideas combine, and all three are already yours. .strip() removes surrounding whitespace (Chapter 4.7). A string with nothing left is empty, and an empty string is false. not inverts that, so the condition reads if there is nothing on this line but whitespace.

Note what if not line: alone would have done: a line of three spaces is not empty, so it would have counted as content. The .strip() is what makes the check mean blank rather than zero-length.

This condition becomes far more useful once Chapter 10.7 gives you a way to skip a line and move to the next one. The condition itself is finished; only its setting is missing.

9. Predict, Run, Observe

Say true or false for each, then check.

Azvya Education Pvt. Ltd.VLSI Mentor
Snippet
print(bool([]))
print(bool([[]]))
print(bool("0"))
print(bool(0.0))
print(bool(" "))
print(bool({"a": 0}))
print(bool(0) or bool("x"))
print(bool(None) == bool(0))

The verified output:

Azvya Education Pvt. Ltd.VLSI Mentor
Snippet
False
True
True
False
True
True
True
True
  • [] is empty — false. [[]] has one item — true.
  • "0" is a one-character string — true. 0.0 is zero — false.
  • " " has a character in it — true.
  • {"a": 0} has one entry — true. The entry's value being falsy is not consulted.
  • False or True is True.
  • bool(None) and bool(0) are both False, so comparing them gives True — which is exactly the collapse Section 6 warned about, written as an equality.

10. Exercises

Exercise 1 — the table from memory. Write down which of these are false, then verify all twelve with bool(): 0, 1, -1, "", " ", "0", [], [0], (), {}, set(), None.

Exercise 2 — say it out loud. For each, choose between if x: and an explicit comparison, and give your reason in one sentence: (a) errors is a count of UVM_ERROR lines (b) width is a signal width where 0 is invalid and must be reported (c) failed is a list of failing test names (d) name is a string read from a file that may be empty (e) index is a position in a list.

Exercise 3 — the configuration trap. A setting is read from a file as the string "False". Show that if setting: takes the true branch, then write a condition that treats the text "False" as off. Explain in one sentence why the first version cannot work.

Exercise 4 — clean or missing. Using counts = {"smoke": 0, "burst": 2}, write a program that prints one of three messages for "reset": no recorded result, clean, or a number of errors. Verify it for all three of "reset", "smoke" and "burst".

Exercise 5 — find the hidden zero. This is meant to report the first position of a failing test. Find the input for which it is wrong, and fix it.

Azvya Education Pvt. Ltd.VLSI Mentor
Snippet
position = tests.index("reset")
if position:
    print("reset is at position", position)
else:
    print("reset is not in the list")

Exercise 6 — == True. For value = 1, value = 3 and value = True, give the result of both if value: and if value == True:. Explain the pattern in two sentences.

Exercise 7 — blank lines. Given the four strings "", " ", "\n" and "x", predict what if not line.strip(): does for each, then verify. Explain what .strip() contributes that if not line: would miss.

Exercise 8 — write the rule. In two sentences and without listing any values, state the rule that decides whether something counts as true in Python. Then name one value your rule gets wrong, or say why you believe there is none.

11. Summary

  • Any value can be a condition. Python asks whether it counts as true, and bool() reports that answer directly.
  • Falsy values: 0, 0.0, "", [], (), {}, set(), None and False. Everything else you have met is true.
  • The rule is one sentence: a value is false when it represents nothing — zero, empty or absent.
  • "0", "False", " " and [0] are all true. Truthiness asks whether the container is empty and never looks inside it.
  • if failed: reads as "if anything failed" and is better than if len(failed) > 0: for exactly that reason.
  • Prefer the short form when the falsy case means "nothing happened", and an explicit comparison when it does not.
  • Falsy is not missing. 0 and None take the same branch, so a clean result and an absent result become indistinguishable — use is None to separate them.
  • A legitimate zero breaks the idiom: an error count of zero, a width of zero and position 0 are all real values that if value: discards.
  • if ready: beats if ready == True:, and not only for brevity — count == True is false for count = 3, which if count: is not.
  • if not line.strip(): detects a blank line, where if not line: would only detect an empty one.

The next chapter takes the same decision and makes it produce a value instead of selecting statements.

Where this fits

Part of the Python curriculum.