Why truthiness works the way it does¶
Python lets you write if items: instead of if len(items) > 0:, and if user: instead of if user is not None:. That feels convenient at first, dangerous on closer inspection, and — once you've sat with it — deliberately chosen.
The design choice¶
Python's designers made a specific decision: every object should be able to answer the question "am I meaningful in this context?" That question has different answers for different types, and the type knows best.
For a list, "meaningful" means "not empty". For a number, "non-zero". For a user-defined class, whatever the class author decides. Truthiness is the protocol that lets every type plug into conditional code without the code needing to know the type.
This is duck typing applied to conditions. If an object quacks like something truthy, it is.
The hooks¶
A class can define two methods to participate in truthiness:
__bool__(self)returnsTrueorFalsedirectly. Use this when the class has a natural notion of "valid" or "present" that isn't about length.__len__(self)returns an integer. If the class is a container,lendoing double duty for truthiness is convenient — you only write one method.
If neither is defined, the default is "always truthy". This is why bool(object()) is True — a bare object instance has no notion of emptiness, so it can't be anything other than truthy.
The trade-off¶
The idiomatic form is concise:
The explicit form is precise:
Both are valid Python. The question is which one you want.
The idiomatic form reads better, matches how most Python is written, and works across any type that implements the truthiness protocol sensibly. The cost is that it collapses distinctions: 0 and None, "" and None, [] and None all look the same to if.
If your code needs to distinguish those cases — and sometimes it does — then is None is the right check.
Using if not name: there would treat "" as "missing" too, which might be what you want, or might not. The explicit check forces you to decide.
The counterargument¶
Some style guides (notably some corporate Python guides) push back on truthiness and prefer explicit comparisons everywhere. The argument: it's clearer to the reader, and it sidesteps the NumPy/Pandas truthiness trap (where arrays and dataframes refuse to answer bool(...) when they have more than one element).
That's a legitimate position. The counterargument is that idiomatic Python is what most Python is, and fighting the convention makes your code read less like Python, not more.
Where most working Python lands: use truthiness for "is this thing meaningful?" checks, use is None when the distinction between "missing" and "empty" matters, and know which of those two questions you're asking.
Where it bites¶
The traps are concentrated in a small set of cases:
- Numeric zero is falsy. A function returning
0will look "empty" toif result:even though the call succeeded. - NumPy and Pandas refuse to coerce multi-element arrays to
bool— they raiseValueError: The truth value of an array ... is ambiguous. Use.any(),.all(), or.emptyinstead. - Custom classes without
__len__are always truthy, which might not be what you want. Add__bool__if the notion of "valid" applies.
Each of these is well-known enough to avoid with a little care. None of them is a reason to abandon the convention; they're reasons to know when you're in one of the sharp corners.
Further reading¶
- Truthiness rules — the canonical falsy list and how
__bool__/__len__interact. - Avoid common conditional mistakes — the sharp corners in practical form.