On Apr 26, 2020, at 16:58, Steven D'Aprano <steve@pearwood.info> wrote:
On Sun, Apr 26, 2020 at 04:13:27PM -0700, Andrew Barnert via Python-ideas wrote:
But if we add methods on zip objects, and then we add a new skip() method in 3.10, how does the backport work? It can’t monkeypatch the zip type (unless we both make the type public and specifically design it to be monkeypatchable, which C builtins usually aren’t).
Depends on how you define monkey-patching.
I'm not saying this because I see the need for a plethora of methods on zip (on the contrary); but I do like the methods-on-function API, like itertools.chain has. Functions are namespaces, and we under-utilise that fact in our APIs.
Namespaces are one honking great idea -- let's do more of those!
Here is a sketch of how you might do it:
# Untested. class MyZipBackport(): real_zip = builtins.zip def __call__(self, *args): return self.real_zip(*args) def __getattr__(self, name): # Delegation is another under-utilised technique. return getattr(self.real_zip, name) def skip(self, *args): # insert implementation here...
builtins.zip = MyZipBackport()
But this doesn’t do what the OP suggested; it’s a completely different proposal. They wanted to write this: zipped = zip(xs, ys).skip() … and you’re offering this: zipped = zip.skip(xs, ys) That’s a decent proposal—arguably better than the one being discussed—but it’s definitely not the same one.
I don't know what "zip.skip" is supposed to do,
I quoted it in the email you’re responding to: it’s supposed to yield short tuples that skip the iterables that ran out early. But from the wording you quoted it should be obvious that isn’t an issue here anyway. As long as you understand their point that they want to leave things open for expansion to new forms of zipping in the future, you can understand my point that their design makes that harder rather than easier.
Also, what exactly do these methods return?
An iterator. What kind of iterator is an implementation detail.
The type of the zip objects is not part of the public API, only the functional behaviour.
Now go back and do what the OP actually asked for, with the zip iterator type having shortest(), equal(), and longest() methods in 3.9 and a skip() method added in 3.10. It’s no longer just “some iterator type, doesn’t matter”, it has specific methods on it, documented as part of the public API, and you need to either subclass it or emulate it. That’s exactly the problem I’m pointing out. The fact that it’s not true in 3.8, it’s not required by the problem, it’s not true of other designs proposed in this thread like just having more separate functions in itertools, it’s specifically a flaw with this design. So the fact that you can come up with a different design without that flaw isn’t an argument against my point, it’s just a probably-unnecessary further demonstration of my point. Your design looks like a pretty good one at least at first glance, and I think you should propose it seriously. You should be showing why it’s better than adding methods to zip objects—and also better than adding more functions to itertools or builtins, or flags to zip, or doing nothing—not pretending it’s the same as one of those other proposals and then trying to defend that other proposal by confusing the problems with it.