On Tue, Aug 22, 2017 at 12:31 PM, Chris Barker <chris.barker@noaa.gov> wrote:
> 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.