Changing calling sequence

2QdxY4RzWzUUiLuE at potatochowder.com 2QdxY4RzWzUUiLuE at potatochowder.com
Sun May 15 09:20:34 EDT 2022


On 2022-05-15 at 14:44:09 +1000,
Chris Angelico <rosuav at gmail.com> wrote:

> On Sun, 15 May 2022 at 14:27, dn <PythonList at danceswithmice.info> wrote:
> >
> > On 15/05/2022 11.34, 2QdxY4RzWzUUiLuE at potatochowder.com wrote:
> > > On 2022-05-15 at 10:22:15 +1200,
> > > dn <PythonList at DancesWithMice.info> wrote:
> > >
> > >> That said, a function which starts with a list of ifs-buts-and-maybes*
> > >> which are only there to ascertain which set of arguments have been
> > >> provided by the calling-routine; obscures the purpose/responsibility
> > >> of the function and decreases its readability (perhaps not by much,
> > >> but varying by situation).
> > >
> > > Agreed.
> > >
> > >> Accordingly, if the function is actually a method, recommend following
> > >> @Stefan's approach, ie multiple-constructors. Although, this too can
> > >> result in lower readability.
> > >
> > > (Having proposed that approach myself (and having used it over the
> > > decades for functions, methods, procedures, constructors, ...), I also
> > > agree.)
> > >
> > > Assuming good names,¹ how can this lead to lower readability?  I guess
> > > if there's too many of them, or programmers have to start wondering
> > > which one to use?  Or is this in the same generally obfuscating category
> > > as the ifs-buts-and-maybes at the start of a function?
> > >
> > > ¹ and properly invalidated caches
> >
> > Allow me to extend the term "readability" to include "comprehension".
> > Then add the statistical expectation that a class has only __init__().

Aha.  In that light, yeah, in geeral, the more stuff there is, the
harder it is to get your head around it.  And even if I document the
class (or the module), no one makes the time to read (let alone
comprehend) the document, which *should* clarify all those things that
are hard to discern from the code itself.

> > Thus, assuming this is the first time (or, ... for a while) that the
> > class is being employed, one has to read much further to realise that
> > there are choices of constructor.
> 
> Yeah. I would generally say, though, that any classmethod should be
> looked at as a potential alternate constructor, or at least an
> alternate way to obtain objects (eg preconstructed objects with
> commonly-used configuration - imagine a SecuritySettings class with a
> classmethod to get different defaults).

I think opening up the class and sifting through its classmethods to
find the factory functions is what dn is talking about.  Such a design
also means that once I have a SecuritySettings object, its (the
instance's) methods include both instance and class level methods.  IMO,
classmethods were/are a bad idea (yes, I'm probably in the minority
around here, but someone has to be).  The first person to scream "but
discoverability" will be severely beaten with a soft cushion.

> > Borrowing from the earlier example:
> >
> > >   This would be quite pythonic. For example, "datetime.date"
> > >   has .fromtimestamp(timestamp), .fromordinal(ordinal),
> > >   .fromisoformat(date_string), ...
> >
> > Please remember that this is only relevant if the function is actually a
> > module - which sense does not appear from the OP (IMHO).

Note that datetime.date is a class, not a module.

> > The alternatives' names are well differentiated and (apparently#)
> > appropriately named*.

[...]

> > Continuing the 'have to read further' criticism (above), it could
> > equally-well be applied to my preference for keyword-arguments, in that
> > I've suggested defining four parameters but the user will only call the
> > function with either three or one argument(s). Could this be described
> > as potentially-confusing?

Potentially.  :-)

In a well designed *library*, common keywords across multiple functions
provide consistency, which is generally good.  Even a bit of redundancy
can be good for the same reason.

OTOH, when there's only one function, and it has a pile of keyword
parameters that can only be used in certain combinations, then it
definitely can be harder to read/understand/use than separate functions
with simpler interfaces.

> Yes, definitely. Personally, I'd split it into two, one that takes the
> existing three arguments (preferably with the same name, for
> compatibility), and one with a different name that takes just the one
> arg. That could be a small wrapper that calls the original, or the
> original could become a wrapper that calls the new one, or the main
> body could be refactored into a helper that they both call. It all
> depends what makes the most sense internally, because that's not part
> of the API at that point.
> 
> But it does depend on how the callers operate. Sometimes it's easier
> to have a single function with switchable argument forms, other times
> it's cleaner to separate them.

"Easier" and "cleaner" are very often orthogonal.  ;-)  (Rich Hickey
(creator of Clojure) talks a lot about the difference between "easy" and
"simple."  Arguemnts for and against Unix often involve similar terms.)

And "easier" or "cleaner" for whom?  The person writing the function(s),
the person writing code that calls the function(s), the person reading
the function(s), the person reading code that calls the function(s), all
or any of that code?  Testers?  Maintainers?  Their code?  Sometimes,
what's easier and cleaner for one is harder and dirtier for another.

As one whose beard likely resembles dn's, I can often spot code written
by developers who have never been involved in testing or maintenance, or
code written by testers who have never written production code (and
don't/won't realize or admit that some tests *are* production code).


More information about the Python-list mailing list