You signed in with another tab or window. Reload to refresh your session.You signed out in another tab or window. Reload to refresh your session.You switched accounts on another tab or window. Reload to refresh your session.Dismiss alert
{{ message }}
Repository navigation
Add __length_hint__ methods to itertools helpers #145361
Following-up on #145333, I would like to add more __length_hint__ methods to itertools helper iterators. Not all of them can have such a method (those that are not finite, like count or unknown like takewhile) but I think we could at least add them for starmap, batched, and possibly others like product, pairwise etc that will require some more attention.
Please, wait before working on it. We need @rhettinger or a maintainer's consensus on that one. I think it's fine but would it be really useful in general cases?
I think it's goood reading overall, some points that seem relevant to this:
Enables a performance optimization: Replace continual re-allocation with an up front (over-)allocation reducing or eliminating copies and reallocations resulting in a significant speedup.
How well passing through / calling __length_hint__ on underlying may work in various cases.
With that in mind I suspect for these would be useful to measure the performance tradeoff between having another function vs. how many elements are in the iterable. My intuition says small iterables (ex. 1-4 items) will be slightly slower while mid to long will see significant speedups. Being explicit about that cost/benefit tradeoff I think will be very useful. Most useful is if there are cases in pyperformance or well used public code bases which will speedup; pyperf microbenchmarks also potentially useful.
That definitely takes work to measure for a proposal and doesn't guarantee acceptance but will likely help get consensus. Significant performance gains in real-world code bases are very motivating :)
Thanks so much for that comment, it's extremely helpful. I'll take some time to unravel that, read the things you recommended and eventually have a go at measuring the impact when I get a chance.
Anecdotally, I was wondering before making the PR whether this would be useful at all so I made some very basic benchmarks on just building lists where I saw noticeable improvement (order of 10-15%), but that was just one specific example.
I checked in some of the bigger repos (using GitHub Code Search) for usage of itertools.islice (which was the first helper I looked into) and overall, it seems like they either don't use it in a way that would benefit from adding __length_hint__ (e.g. they do next(islice(...)) which doesn't materialise the iterator) or use it in contexts where it would be useful, but the workload is already heavily I/O bound (e.g. Django batching database operations if I remember correctly).
For completeness' sake, I also checked usages in the standard library and while there are some, there aren't a lot and most importantly they're on lower-sized inputs as far as I can tell (which, I've noticed in benchmarks, aren't benefitting from the optimisation).
After that, I figured microbenchmarks might be the logical next step.
I had already written some previously (just to check that my changes were actually somewhat useful, before opening my PR) and they confirmed what Cody said: on smaller-sized iterators (up to 10 items, I'd say), there is a performance hit (coming I assume from one additional call). We're talking ~10% of O(100ns) calls so it's not huge, but it's there.
On larger iterators, there's a significant performance boost (~15-25% as early as 100 items), then eventually on even larger ones (100k+), it seems like the performance gains from making islice more efficient are less and less significant compared to the overall cost of allocating.
Since I was interested in knowing more about other helpers as well, I implemented a __length_hint__ for starmap, and accumulate. The bottomline for starmap and accumulate is that they show the same results approximately as islice.
So long story short... I'm not sure those changes are worth making, after all. Happy to hear(/read) your thoughts if you think otherwise but I'm especially not a fan of that fact it makes performance worse in some probably common cases (few items).
For future reference, my benchmark for islice was the following. The only "benchmarked variable" is stop because that's the one that varying has the most effect on performance (since it directly has an impact on the number of resulting items).
Activity
picnixz commentedon Feb 28, 2026
Please, wait before working on it. We need @rhettinger or a maintainer's consensus on that one. I think it's fine but would it be really useful in general cases?
cmaloney commentedon Mar 1, 2026
Some background bits:
__length_hint__was added in PEP-424, discussed at https://mail.python.org/pipermail/python-dev/2012-July/120920.html, and implemented in gh-60352.I think it's goood reading overall, some points that seem relevant to this:
__length_hint__on underlying may work in various cases.With that in mind I suspect for these would be useful to measure the performance tradeoff between having another function vs. how many elements are in the iterable. My intuition says small iterables (ex. 1-4 items) will be slightly slower while mid to long will see significant speedups. Being explicit about that cost/benefit tradeoff I think will be very useful. Most useful is if there are cases in pyperformance or well used public code bases which will speedup;
pyperfmicrobenchmarks also potentially useful.That definitely takes work to measure for a proposal and doesn't guarantee acceptance but will likely help get consensus. Significant performance gains in real-world code bases are very motivating :)
KowalskiThomas commentedon Mar 1, 2026
Thanks so much for that comment, it's extremely helpful. I'll take some time to unravel that, read the things you recommended and eventually have a go at measuring the impact when I get a chance.
Anecdotally, I was wondering before making the PR whether this would be useful at all so I made some very basic benchmarks on just building lists where I saw noticeable improvement (order of 10-15%), but that was just one specific example.
I'll update this ticket with my progress !
rhettinger commentedon Mar 2, 2026
I explored this path at one point and mostly found it to be a dead-end. I wrote-up some of my findings in
Lib/test/test_iterlen.py.KowalskiThomas commentedon Mar 2, 2026
To follow-up on this...
I checked in some of the bigger repos (using GitHub Code Search) for usage of
itertools.islice(which was the first helper I looked into) and overall, it seems like they either don't use it in a way that would benefit from adding__length_hint__(e.g. they donext(islice(...))which doesn't materialise the iterator) or use it in contexts where it would be useful, but the workload is already heavily I/O bound (e.g. Django batching database operations if I remember correctly).For completeness' sake, I also checked usages in the standard library and while there are some, there aren't a lot and most importantly they're on lower-sized inputs as far as I can tell (which, I've noticed in benchmarks, aren't benefitting from the optimisation).
After that, I figured microbenchmarks might be the logical next step.
I had already written some previously (just to check that my changes were actually somewhat useful, before opening my PR) and they confirmed what Cody said: on smaller-sized iterators (up to 10 items, I'd say), there is a performance hit (coming I assume from one additional call). We're talking ~10% of O(100ns) calls so it's not huge, but it's there.
On larger iterators, there's a significant performance boost (~15-25% as early as 100 items), then eventually on even larger ones (100k+), it seems like the performance gains from making
islicemore efficient are less and less significant compared to the overall cost of allocating.Since I was interested in knowing more about other helpers as well, I implemented a
__length_hint__forstarmap, andaccumulate. The bottomline forstarmapandaccumulateis that they show the same results approximately asislice.So long story short... I'm not sure those changes are worth making, after all. Happy to hear(/read) your thoughts if you think otherwise but I'm especially not a fan of that fact it makes performance worse in some probably common cases (few items).
For future reference, my benchmark for
islicewas the following. The only "benchmarked variable" isstopbecause that's the one that varying has the most effect on performance (since it directly has an impact on the number of resulting items).Details
rhettinger commentedon Mar 2, 2026
Thank you for the research effort.
I agree. Let's close this.