PEP 308: some candidate uses cases from live code

Martin Maney maney at pobox.com
Sun Feb 9 03:10:17 EST 2003


Okay, a bleary-eyed survey of about 4000 lines of code from a project
I've been working on in odd bits of time.  Best Available Data, sir,
but mine own.  <wink>

These are the uses of a simple if/else that seemed to me to be at least
vaguely suitable for the proposed ternary operator; most of the if/else
occurences rejected were over four lines or had very little in common
between the if and the else code.  I don't believe I omitted any that
weren't less good candidates than the ones below.

If you think my commentary reveals an imperfectly consistent view of
the proper use and value of 308's ternary, you may be right.  I worked
on this intermittently over the last few hours, and I haven't gone back
and "corrected" anything [though I may add a note like this before I
send this off] just because I was learning how I liked this as I went
along.


Candidate 1 - this is from a simple "one line == one record" CSV parser

        else:
            start = i
            j = s.find(',', i)
            if j >= 0:
                i = j
            else:
                i = n
            field = s[start:i]

    About all that could be changed here is collapsing the if/else, as i is
    live past the end of the code shown:

        else:
            start = i
            j = s.find(',', i)
            i = j if j >= 0 else n
            field = s[start:i]

    I don't find this very convincing, but it has its points.  I wrote it so
    as to keep the condition the same, and it's just coincidence that that
    made it come out with the pleasing "j if j >= 0 ..." form.  At least I
    find it pleasing.  Is that line harder to understand than any one of the
    original lines?  Of course.  Is it harder to understand than the total
    effect of the four lines it replaces?


Candidate 2 - generating bounding dates for a month

        first = '%04d-%02d-01' % (year, month)
        if (month < 12):
            last = '%04d-%02d-01' % (year, month + 1)
        else:
            last = '%04d-01-01' % (year + 1)

    Not really a very promising candidate: there's too much that's different
    between the two values for this to appeal to me.  This was the second
    plausible candidate I found after sifting through a lot of Python with
    no simple if/else constructs.  Of course a lot of the code I've been
    looking at was written knowing that ternary wasn't a viable option (I
    have used one or another of the kulges a few times, but generally find
    them uglier than four not-quite-emtpy lines of code.)


Candidate 3 - calculate "school year" from year & month

        if d0.month < 7:
            sy = d0.year - 1
        else:
            sy = d0.year

    Becomes, after a little rearrangement:

        sy = d0.year - (1 if d0.month < 7 else 0)

    I think this actually expresses the original logic a little more
    clearly: the school year is the day's year unless the day's month is
    January through June; then one must be subtracted to get the calendar
    year during which the school year began.


Candidate 4 - preparing to use an optional parameter in generated HTML

        if params.has_key('add2url'):
            add2url = '?' + params['add2url']
        else:
            add2url = ''

    Could be expressed as

        add2url = '?' + params['add2url'] if params.has_key('add2url') else ''

    This is an example where the short-circuit behavior is important, but
    it's not a terribly convincing example overall since it could easily
    be written as

        add2url = ''
        if params.has_key('add2url'):
            add2url = '?' + params['add2url']

    This last is almost certainly how I would write this, as there's no
    concrete advantage IMO to the ternary form here.  I think I was using
    a common pattern for all setup items in this routine and/or module.

    [later: "certainly"?  well, maybe, but maybe not]


Candidate 5 - another bit from HTML generation

        if len(events) > 1:
            sep = ' '
        else:
            sep = '<br>'

    Obvious:

        sep = ' ' if len(events) > 1 else '<br>'

    In a larger context, this would actually allow me to do away with the
    'sep' temporary:

        if len(events) > 1:
            sep = ' '
        else:
            sep = '<br>'
        ...
        if tail:
            tail = sep + tail

    allowing a considerable reduction:

        ...
        if tail:
            tail = (' ' if len(events) > 1 else '<br>') + tail

    I'm not sure why the calculation of 'sep' was separated from its use
    in the code.  This is older code that's been through a couple waves of
    refactorings of the system its part of; possibly 'sep' used to be used
    in more than one location in an earlier version.  Perhaps the inlined
    version above should be compared to this rather than the original:

        ...
        if tail:
            if len(events) > 1:
                tail = ' ' + tail
            else:
                tail = '<br>' + tail


Candidate 6 - again, HTML generation

        if colors:
            color = 'bgcolor=%s' % colors[gr % len(colors)]
        else:
            color = ''

    Naturally:

        color = 'bgcolor=%s' % colors[gr % len(colors)] if colors else ''

    Hmmm... what precedence does ternary have, again?  The previous item
    just seemed to obviously need the parens; this one I'm not so sure
    about.


Summary:

    I didn't find nearly as many simple if/else uses as I had expected to
    in this project's code.  What else have I been working on recently that
    might account for this persistent notion that I ought to have better
    examples than most of these?

    The other thing I discovered was that some of the older modules really
    need a good, brisk currying - I came across a few things that made me
    wince.  Oh, and Aahz, I know you'll want to hear about this: a couple
    modules with some map'n'filter'n'lambda uses that I'm certain will look
    a lot better as list comprehensions.  :-)

    Amazing how dated six month old code can look...





More information about the Python-list mailing list