On Fri, May 15, 2020 at 08:17:12AM +0100, Paul Moore wrote:
> 1. Why do we want to "tempt" people to error when handling mismatched
> lengths? Certainly letting people catch errors easily is worthwhile,
> but rejecting arguments of different lengths may well *not* be an
> error ("be lenient in what you accept" is a well-known principle, even
> if not something that everyone agrees on in all cases).
I concur with Paul here.
There may be cases where mismatched lengths are an error, but there are
also cases where they are not an error. It is patronising to be talking
about tempting people into using the strict version of zip. People who
need it will find it, whether it is builtin or not.
> 2. I find "mode switch" arguments ugly
I think we need to distinguish between *modes* and *flags*. This
proposed functionality is a mode, not a flag.
Mode switches are extensible:
zip(*args, mode='strict')
can easily be extended in the future to support new modes, such as
zip_longest. There are at least two other modes that have been mentioned
briefly in the Python-Ideas thread:
1. a variety of zip that returns information in the StopIterator
exception identifying the argument that was empty and those that
weren't;
2. a variety of zip that simply skips the missing arguments:
zip_skip('abc', 'ef', 'g')
=> (a, e, g) (b, f), (c,)
I don't even understand the point of the first one, or how it would
operate in practice, or why anyone would need it, but at least one
person thinks it would be useful. Take that as you will.
But I have written my own version of the second one, and used it.
A mode parameter naturally implies that only one mode can apply at a
time, and it naturally enforces that restriction without any additional
programmer effort. If there are (let's say...) four different modes:
mode = shortest|longest|strict|skip
you can only supply one at a time.
Using *named modes* (whether as strings or enums) to switch modes is
quite reasonable. It's not my preferred API for this, but I don't hate
it.
But a *flag* parameter is difficult to extend without making both the
API and the implementation complicated, since any extension will use
multiple parameters:
zip(*args, strict=True, longest=False, skip=True)
and the caller can supply any combination, each of which has to be
tested for, exceptions raised for the incompatible combinations. With
three flags, there are eight such combinations, but only four valid
ones. With four flags, there are 16 combinations and only five
meaningful combinations.
Flags work tolerably well when the parameters are independent and
orthogonal, so you can ask for any combination of flags.
But flags to switch modes are *not* independent and orthogonal. We can't
combine them in arbitrary combinations. Using *boolean flags* to switch
modes in this way makes for a poor API.
For the record, my preferred APIs for this are in order:
1. +1 itertools.zip_strict function
2. +1 zip.strict(*args)
3. +1 zip(*args, mode='strict') # mode='shortest' by default
4. +0 zip(*args, strict=True)
and even that is being generous for option 4.
Note that options 1 and 2 have an important advantage over options 3 and
4: the strict version of zip is a first-class callable object that can
be directly passed around and used indirectly in a functional way.
E.g. for testing using unittest:
assertRaises(zip.strict, 'a', '', Exception)
Yes, assertRaises is also usable as a context manager, but this is just
an illustration of the functional style.
--
Steven
_______________________________________________
Python-Dev mailing list -- [email protected]
To unsubscribe send an email to [email protected]
https://mail.python.org/mailman3/lists/python-dev.python.org/
Message archived at
https://mail.python.org/archives/list/[email protected]/message/ZSOFQ5MQLQOBYC4GWNFBBPYEWUNNCYKY/
Code of Conduct: http://python.org/psf/codeofconduct/