On Apr 21, 2020, at 19:35, Steven D'Aprano <steve@pearwood.info> wrote:
On Mon, Apr 20, 2020 at 07:47:51PM -0700, Andrew Barnert wrote:
counter = itertools.count() yield from zip(counter, itertools.chain(headers, [''], body, ['']) lines = next(counter)
That gives you one more than the number of lines yielded.
Yeah, I screwed that up in simplifying the real code without testing the result. And your version gives one _less_ than the number yielded.
No, my version repeats the last number yielded, which is precisely what you wanted (as I understand it).
No, I wanted the number of lines yielded. You not only quoted that, but directly claimed that you were giving the number of lines yielded. But you’re not; you’re giving me the number of the last line, which is 1 less than that.
py> def test(): ... headers = body = '' ... for t in enumerate(itertools.chain(headers, [''], body, [''])): ... yield t ... print(t[0]) ... py> list(test()) 1 [(0, ''), (1, '')]
Right. The number of pairs yielded is 2. Your code prints 1.
(With either enumerate(xs) or zip(counter, xs) the last element will be (len(xs)-1, xs[-1]).
Um, yes? That's because both enumerate and counter start from zero by default. I would have asked you why you were counting your lines starting from zero instead of using `enumerate(xs, 1)` but I thought that was intentional.
You were right, counting from 0 was intentional. Just as it is almost everywhere in Python. The caller needs those line numbers; otherwise I wouldn’t be yielding them in the first place. And that’s why your solution is wrong: you correctly left it counting from 0, but then incorrectly assumed that the last number equals the count, which is only true when counting from 1. If that’s not a classic fencepost error, I don’t know what is. And my originally-posted version has a different fencepost error, as you pointed out. And my real code doesn’t, but I may well have made one and had to spend a minute debugging it. Nontrivial counting code often has fencepost errors, and Python only eliminates the sources that come up often, not every possible one that might come up rarely, which is fine. And this proposal doesn’t change that in any way, nor is it meant to.
Your version has the additional problem that if the iterable is empty, t is not off by one but unbound (or bound to some stale old value)—but that’s not possible in my example, and probably not in most similar examples.
But the iterable is never empty, because you always yield at least two blanks.
Yes; I said “but that’s not possible in my example”, as you quoted directly above.
I don't believe this zip_strict proposal would help you in this situation. I think it will make it worse,
Well, of course. Since it wasn’t an argument for the proposal, but an example pointing out a potential hole in the proposal that needed to be thought through, why would you expect the proposal to help it? To recap: Someone had said that it doesn’t matter what state the iterables are left in, because nobody ever looks at an iterator after zip. So I gave an example of (simplified) real code that looks at an iterator after zip. So people thought through what state the iterables should be left in by this new zip_strict function, and there is a reasonable answer. Even if your arguments about this example were correct, they wouldn’t be relevant to the thread, because the entire purpose of giving the example has been fulfilled.