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