> Personally, I've thought for years that Python's "Truthiness" concept is a wart.
> Sure, empty sequences, and zero values are often "False" in nature,
> but truthiness really is application-dependent -- in particular, sometimes
> a value of zero is meaningful, and sometimes not.
I think truthiness is easily a wart in any dynamically-typed language (and yet ironically, every language I can think of that has truthiness is dynamically typed except for C++). And yet for some reason it seems to be pressed forward as idiomatic in python, and for that reason alone, I use it. These are questions I ask myself on a daily basis, just to support this strange idiom:
- How close to the public API is this argument?
- Is '' a reasonable value for this string?
- How about an empty tuple? Empty set?
- Should this sentinel value be None or a new object()?
- Is this list local to this function?
- Is the type of this optional argument always True?
- How liable are these answers to change with future refactoring?
which seems like a pretty big laundry list to keep in check for what's supposed to be syntactic sugar. In the end, I will admit that I think my code "looks nice," but I think that's only because I've gotten used to seeing it!
After answering all of these questions I tend to find that truthiness is seldom usable in any sort of generic code. These are the kinds of places where I usually find myself using truthiness instead, and all involve working with objects of known type:
# 1. A list used as a stack
while stack:
top = stack.pop()
...
def read_config(d):
# 2. Empty default value for a mutable argument that I don't mutate
d = dict(d or {})
a = d.pop('a')
b = d.pop('b')
...
# 3. Validating configuration
if d:
warn('unrecognized config keys: {!r}'.format(list(d)))
# 4. Oddball cases, e.g. the "linked list" (a, (b, (c, (d, (e, None)))))
def iter_linked_list(node):
while node:
value, node = node
yield value
# 5. ...more oddball stuff...
def format_call(f, *args, **kw):
arg_s = ', '.join(repr(x) for x in args)
kw_s = ', '.join(f'{k:!s}={v:!r}' for k,v in kw.items())
sep = ', ' if args and kw else ''
return f'{f.__name__}({arg_s}{sep}{kw_s})'
Meanwhile, for an arbitrary iterator taken as an argument, if you want it to have at least one element for some reason, then good luck; truthiness will not help you.