"Humane" programmer interfaces

Alex Martelli aleax at mail.comcast.net
Mon Jan 2 14:50:21 EST 2006

Andrew Durdin <adurdin at gmail.com> wrote:

> On 29 Dec 2005 09:50:57 -0800, colinwb <colinwb at yahoo.co.uk> wrote:
> >
> > >> puts ck.first, ck[0], '*', ck.last, ck[-1]
> One of the points at issue (minimalism/monotony) relates to TOOWTDI,
> which has implications for language/module design and for code
> readability. Ruby supports negative indices in the same way as Python,
> so why add .last as an alias for [-1]? It's just added mental baggage.

In this particular case, it can be opined that it's more readable to
code goo.last than goo[-1].  For an analogy, consider, in Python,
somestr.startswith('glab') as a more readable equivalent of
somestr[:4]=='glab' -- the "why add?" question is easily answered,
although it's of course possible to disagree with the specific decision
made in each and every single case in which the same functionality is
provided in two distinct ways.

Python has a LOT of cases in which you can get the same functionality in
distinct ways, and not all of them are judged to fall afoul of the
"there should ideally be only one way" principle.  Operations that have
to do with slicing of sequences are a substantial class of this "added
mental baggage", and not just for strings' endswith and startswith
methods.  Consider, for example:

del somelist[start:finish]
somelist[start:finish] = []

exactly identical semantics.  Or:

somelist[len(somelist):] = another

or (for suitable values of i, only):

somelist.insert(i, blah)
somelist[i-1:i] = [blah]


acopy = somelist[:]
acopy = list(somelist)

In each case, one could argue that the named alternative (del, extend,
insert, list, ...) is more readable; but the slice-based alternative is
also provided because slicing is such a powerful and general tool, and
when you're computing the slice's boundaries at runtime it's precious.

So, I don't think it would violate the spirit of Python to have
somelist.last mean the same as somelist[-1], e.g.:

class listwithlast(list):
    def getLast(self): return self[-1]
    def setLast(self, value): self[-1] = value
    last = property(getLast, setLast)

Such tradeoffs need to be decided case by case, and, in Python, they
have been -- e.g., there's no "somelist.getodd()" synonym for
somelist[::2], for example;-).  I think the myriad specific decisions
and trade-offs may be disputed (and it's not impossible that a strong
enough case can be made for one more such "synonym", so that a PEP
proposing it might in theory be accepted for a future Python version),
but essentially only on a case by case basis (it's good to keep in mind
principles such as "all other things being equal it's better to have one
way than more than one", of course, while taking such decisions).

I do draw the line at foo.size being defined as identical to foo.length,
which I personally classify as a design error (I don't think Python is
exempt from design errors at this minute level, either -- e.g., the
method name 'setdefault' is murky, though I can't propose a better one,
and the existence of both pop and popitem in dictionaries appears to
cause more confusion than benefit, where it might have sufficed to make
pop's argument optional if one wanted popitem's functionality).

> And if you think .last is significantly more readable or convenient
> than [-1], why not add .secondlast ?  And .first, and .second ?   With
> one simple, obvious, general interface, these special cases only add
> unneeded complexity.

first and last make sense (though it's debatable whether they should
exist or not, it's not obvious that they're design errors, not at all);
second, penultimate, and other variations, would be gilding the lily.
Just like startswith and endswith may (debatably) make sense, but, say,
hasrightinthemiddle would be surely inappropriate;-)


More information about the Python-list mailing list