Python synergies in list addressing
Something I took from this Ian Bicking entry is that synergies and elegance don't just happen; someone usually worked hard to make it all come out neatly. Python lists have an interesting case of this; some apparently odd decisions in other places turn out to be needed to create useful (and error-avoiding) synergies.
Python lists (and sequences in general) are indexed from 0. Zero-based
indexing presents a problem, which can be succinctly stated as this:
list element indexes run from 0 to
-1 is ugly
and error prone.
So Python has quietly arranged things so that you never have to write it
+1, its kissing cousin), by making 'slice' addressing of lists and
range() end-exclusive asymmetric (instead of '
i:j' running from
j, it runs from
j-1). This means:
range(len(lst))generates indexes that exactly cover the list, since they run from 0 to
lst[:len(lst)]is the entire list.
prefis at the start of the list,
lst[len(pref):]is the list with
prefremoved from the start.
lst[:pos]is everything before
lst[pos + len(sublst):]is everything after
sufis at the end of
lst[:-len(suf)]is the list with
sufremoved from the end.
- avoiding a subtler error,
lst[len(pref):]works even if
prefis zero length (although
lst[:-len(suf)]does not; can't win them all).
All of these are straightforward expressions, with nary a stray
+1 in sight and no chance for off by one errors. (Is it elegant
or does the asymmetry cancel out the lack of
-1? That's in
the eye of the beholder, but I like it.)
Python is not the first language to notice this issue; Scheme's
substring is end-exclusive, for example. Other things duck the issue
by having their substring operation take a start and a length, instead
of a start and and end position.
(This entry's genesis came from comments made on AClosureConfusion.)