Python’s Truthiness: A Code Smell Worth Sniffing

Python lets you use any value in if and while conditions, not just Booleans. This permissiveness relies on a concept known as “truthiness”. Values like None, 0, "", and empty collections are treated as false and everything else is true.

It’s convenient. It’s idiomatic. It’s also, I argue, a code smell.

The Bug That Changed My Mind

Here’s a real example from one of my side projects:

def final_thing_count() -> Optional[int]:
    """Returns an integer count of things, but only
    if the number of things is complete. If there may
    yet be things to come, returns None to indicate 
    the count isn't final."""
    # Code Redacted.

current_thing_count: Optional[int] = final_thing_count()
if current_thing_count:
    # Process the final count of things.

Looks fine, right? But it’s broken.

If final_thing_count() returns 0, a perfectly valid and reasonable number of things, the condition fails. The code treats it the same as None, which was meant to signal “not ready yet”. The fix?

if current_thing_count is not None:
    # Process the final count of things.

Now the intent is clear. We care whether the value is None or not, not whether it’s “truthy.” Zero is no longer misclassified.

Even if the function never returns zero, I’d still argue the revised version is better. It’s explicit. It’s readable. It doesn’t require the reader to know Python’s rules for truthiness.

I’ve looked at a wide variety of Python code, and every time I see truthiness used, I ask if this be clearer if it were explicit. The answer is almost always yes. The one exception I’ve observed is this use of the or operator…

# Read string, replacing None with an empty string.
my_string = read_optional_string() or ""

This is elegant and expressive. If Python ever adopted stricter Boolean rules, I’d hope this idiom would be preserved or replaced with a dedicated None-coalescing operator.

“I used to know who I was. Now I look in the mirror and I’m not so sure. Lord, I don’t want to listen to the lies anymore!”

C# Chose Clarity Over Convenience

In C, you could write terse, expressive loops like this:

while (*p++) { /* do something */ }

This worked because C, similar to Python, treated any non-zero value as true, and NULL, 0, '\0', and 0.0 as false. It was flexible but also fragile. A mistyped condition or misunderstood return value could silently do the wrong thing.

C# broke from that tradition. It introduced a proper Boolean type and made it a requirement for conditional expressions. If you try to use an int, string, or object directly in an if statement, the compiler rejects it.

Yes, we lost the ability to write one-line loops that stop on a null. But we gained something more valuable, a guardrail against a whole class of subtle bugs. The language nudges you toward clarity. You must say what you mean.

I believe that was the right trade.

A Modest Proposal (and a Practical One)

I’d love it if Python could raise an error when a non-Boolean value is used in a Boolean context. It would need to be an opt-in feature via a “__future__” import or similar, as it would break too much otherwise.

This would allow developers to opt into a stricter, more explicit style. It would catch bugs like the one I described and make intent more visible to readers and reviewers.

As a secondary proposal, linter applications could help bridge the gap. They could flag uses of implicit truthiness, especially in conditionals involving optional types, and suggest more explicit alternatives. This would nudge developers toward clarity without requiring language changes.

Until then, I’ll continue treating truthiness as a code smell. Not always fatal, but worth sniffing.

“I saw your picture in the magazine I read. Imagined footlights on the wooden fence ahead. Sometimes I felt you were controlling me instead.”

Credits
📸 “100_2223” by paolo. (Creative Commons)
📸 “Elephant” by Kent Wang. (Creative Commons)

Leave a Reply

Your email address will not be published. Required fields are marked *