Library / SDK
more-itertools/more-itertools avatar
more-itertools/more-itertools

more-itertools groups its API by shape of problem, not by return type

More routines for operating on iterables, beyond itertools

4,098 stars383 forksPythonMIT

At a glance

What is it?
more-itertools/more-itertools adds building blocks to Python's itertools, and the interesting part is the taxonomy it uses to organise them. Names are grouped by the problem you are solving, splitting a sequence, peeking at what comes next, or sliding a window, so the question you are asking leads you to the function rather than the other way round.
Who is it for?
more-itertools suits a Python developer who keeps writing the same slicing loop and wants a named function for it, and who values the fact that the extra layer is still lazy rather than materialising the iterable. It suits a team that wants a dependency-free utility library with a stable release cadence and a documented API surface rather than a framework.
Can I use it commercially?
Yes. MIT is a permissive licence: you can use, modify and sell software built on it, as long as you keep its copyright and licence notices.
Is it still maintained?
Yes. The repository last received commits 5 days ago.
What is it written in?
Mainly Python, according to GitHub's language statistics.

Answers come from the project's GitHub data, last synced on October 4, 2026, and from our analysis. They are not legal advice.

Editorial analysis

The API is indexed by problem shape, which is the whole navigation scheme

The first thing the project does is group its functions by the shape of the problem rather than alphabetically or by return type. Grouping holds the splitting and batching family: chunking in several flavours, slicing, distributing, dividing, splitting at a value, splitting before or after a predicate, splitting by count, splitting when a predicate flips, bucketing, transposing, batching, grouping, and partitioning. Lookahead and lookback is three functions: one that wraps an iterable so you can inspect without consuming, one that gives an explicit peek interface, and one that adds seeking. Windowing is nine: fixed-size windows, substrings of a string, substring positions, staggered values, windows with padding, pairwise and triplewise iteration, a sliding window that reuses its buffer, and slices of slices. Reading the table in that order tells you more about the library than any feature list would.

Four chunking variants exist because the edge cases differ

One group contains four chunking functions, which is the clearest example of the library's approach. There is the plain one that yields lists of a fixed size with a possibly short final group. There is a lazy version that yields iterables instead of lists, so a chunk does not have to be realised in memory. There is a variant that balances the final group rather than letting it be short, for when a ragged tail is undesirable. And there is a version that takes a predicate for where to cut, rather than a count. That is four names for what a newcomer would think is one operation, and each exists because the obvious one has a different failure mode. The same pattern repeats elsewhere in the table: the split family has a cut-at-value version, before and after predicate versions, a by-count version and a when-the-predicate-flips version, and the documentation is where you find out which one your input shape wants.

Windowing reuses a buffer in one variant and allocates in the others

The windowing group is the largest and contains the distinction worth knowing. A sliding window function is described as reusing its buffer, which is the difference between a window operation that is cheap on a long stream and one that copies on every step. The plain fixed-size window is the general case, and there are variants that pad short inputs so the output length is predictable, which matters when you are indexing the results. Substrings and substring positions do the same job for strings, and stagger produces interleaved offset views rather than a single window. Pairwise and triplewise are the two- and three-element cases that newer Python versions added to the standard library, so they exist here for compatibility with older interpreters as much as anything else. The lesson from this group is that a function's cost is part of its contract, and this library is careful to give you a cheap variant when one exists.

Peeking and seeking are separated because they are different capabilities

The lookahead group has three members and the differences are about mutability, not about syntax. Wrapping an iterable in an object that supports inspecting without consuming is the simplest of the three. A peek interface on top of that gives explicit next and peek operations. The third adds seeking, which is the capability that actually requires buffering: if you can seek backwards, something has to be held. That is why seeking lives in its own function rather than as a flag on the peekable one, and it is the kind of design decision that only shows up when you realise a peekable object cannot un-consume what it already gave you. For anything that reads a stream and occasionally needs to back up a few items, this group is the reason the library is worth a dependency rather than a five-line function in your own code.

Coverage is a build gate, not a report

The development makefile is where this project states its own standards, and one number is the important one. The coverage target installs the testing requirements, runs the standard library test runner with coverage limited to the library's own modules, and then reports missing lines with a flag that fails the build below ninety-nine percent. Ninety-nine is not a coverage target, it is a gate: a pull request that drops a line of coverage fails. The same target runs against two interpreters separately, which is the cheapest way to catch a version-specific behaviour without a matrix. The check target adds a formatter in check mode, the linter over the library and its tests, and a stub consistency test that compares the package against its type stubs. Two specific lint codes are switched off and one per-file exemption exists for the package's re-export module, which is where a wildcard import is deliberate.

Packaging is a two-file setup with the version read from the source

The build configuration is deliberately small. The build backend is a single module, the project metadata names one author, the readme is the reStructuredText source at the repository root, and the licence is declared with the file globbed rather than the text pasted in. Both the version and the description are dynamic, which for this backend means they are read from the package source rather than restated in the metadata, so there is one place to change them. The package itself is a single importable module inside a directory named with an underscore, and the linter is told to target the current Python version with a line length of seventy-nine, which is the same width as the documentation table above. The interpreter floor is three point ten, with classifiers for three point ten through three point fourteen and for both the reference implementation and an alternative one.

Every check runs in one target, and documentation builds with warnings as errors

There is a single aggregate target that runs five things in order: the requirements step, the coverage step, the check step, the docs step and the package step. Each of those is also available on its own, which is what makes the aggregate useful for continuous integration rather than merely convenient. The docs step is the one with a detail worth copying: the documentation builder is invoked with a flag that turns warnings into errors, so a broken cross-reference fails the build rather than shipping a warning nobody reads. The package step installs the packaging requirements, builds the distribution and then validates the built artifacts with a separate checker. The requirements are split into separate files for development and for testing, and a third for packaging, so a contributor installing everything does not need the release tooling and a release manager does not need the test matrix.

Editorial conclusion

more-itertools suits a Python developer who keeps writing the same slicing loop and wants a named function for it, and who values the fact that the extra layer is still lazy rather than materialising the iterable. It suits a team that wants a dependency-free utility library with a stable release cadence and a documented API surface rather than a framework. It does not suit someone who wants async iteration or anything outside the standard protocol, because the whole library operates on the ordinary iterable protocol. It also does not suit a project that cannot accept a hard coverage floor, since the build fails below a declared threshold. Before adopting it, skim the grouping table rather than the alphabetical index, because the names are misleading in isolation: several splitting functions differ only in whether the boundary is included, and a similar case applies to the windowing family.

Frequently asked questions

What is meant by itertools in Python?

It is the standard library module for working with iterables, built around lazy generators and combinators. This project does not replace it; it collects additional building blocks, recipes and routines for working with iterables beyond what that module provides.

Is Python itertools built in?

Yes, itertools ships with Python itself. This project requires Python 3.10 or newer and adds nothing to the standard library; it is a separate installable package that extends the same idea with recipes the standard library does not cover.

How do I install more-itertools?

It is a normal package on the package index with no dependencies of its own. The build backend is a single module, the supported interpreters are 3.10 through 3.14, and the project publishes documentation built with a host that runs on the read-the-docs service.

What is the difference between the chunking functions in more-itertools?

There are four. One yields lists of a fixed size with a possibly short final group, one yields lazy iterables instead of lists, one balances the final group rather than leaving it short, and one takes a predicate deciding where to cut instead of a count. They exist separately because the obvious one has different edge-case behaviour in each of those cases.

Official sources

  1. License: MIT
  2. more-itertools/more-itertools on GitHub
  3. Project website
  4. README
  5. Releases
Add this badge to your README

If you maintain this project, the badge below links readers to this analysis and shows its maintenance status from the daily GitHub snapshot. Paste the markdown into your README; add ?metric=license or ?metric=stars to the image URL for a different field.

Add this badge to your README

markdown
[![Hysen Labs](https://hysenlabs.com/badge/more-itertools-more-itertools.svg)](https://hysenlabs.com/projects/more-itertools-more-itertools)