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 -- python-dev@python.org
To unsubscribe send an email to python-dev-le...@python.org
https://mail.python.org/mailman3/lists/python-dev.python.org/
Message archived at 
https://mail.python.org/archives/list/python-dev@python.org/message/ZSOFQ5MQLQOBYC4GWNFBBPYEWUNNCYKY/
Code of Conduct: http://python.org/psf/codeofconduct/

Reply via email to