Re: "Humane" programmer interfaces



Andrew Durdin <adurdin@xxxxxxxxx> wrote:

> On 29 Dec 2005 09:50:57 -0800, colinwb <colinwb@xxxxxxxxxxx> 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.extend(another)
somelist[len(somelist):] = another

or (for suitable values of i, only):

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

or

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;-)


Alex
.



Relevant Pages

  • Re: Will multithreading make python less popular?
    ... I hope someone can fix these design errors and maybe ... can write an interpreter in python:) ...
    (comp.lang.python)
  • Parallel/Multiprocessing script design question
    ... The processing involves multiple steps that each input file has to go ... I am trying to decide between a batch mode design and a pipelined ... But I had chosen Python over Perl ... steps and have multiple such "pipelines" running simultaneously in parallel. ...
    (comp.lang.python)
  • Re: Attack a sacred Python Cow
    ... interest primarily to computer scientists and language enthusiasts. ... Transparently exposing most of it's object model is a design choice, and is for a great part responsible for Python expressive power and flexibility. ... designed to encourage bad programming practices. ...
    (comp.lang.python)
  • Re: Handling Property and internal (__) attribute inheritance and creation
    ... I've been thinking in circles about these aspects of Pythonic design ... them from a child class method. ... Of course, like anything else in Python, these ... can override the methods. ...
    (comp.lang.python)
  • Re: case/switch statement?
    ... > performance is not a goal for reasons of *readability*. ... > surrounding ones (in Python or a language with explicit block ... with variably formated records where each record contains a record type flag. ... if the translation is pretty much one to one then the argument ...
    (comp.lang.python)