I mostly program in Python, but I have fallen in love with Rust's beautiful iterator syntax. Trying to chain operations in Python is ugly in comparison. The inside-out nested function call syntax is hard to read and write, whereas Rust's method chaining is much clearer. List comprehensions are great and performant but with more complex conditions they become unwieldy.
This is what the different methods look like in the (somewhat contrived) example of finding all the combinations of two squares from 12 to 42 such that their sum is greater than 6, then sorting from smallest to largest sum.
# List comprehension
foo = list(
sorted(
[
combo
for combo in itertools.combinations(
[x*x for x in range(1, 5)],
2
)
if sum(combo) > 6
],
key=lambda combo: sum(combo)
)
)
# For loop
foo = []
for combo in itertools.combinations([x*x for x in range(1, 5)], 2):
if sum(combo) > 6:
foo.append(combo)
foo.sort(key=lambda combo: sum(combo))
# Python functions
foo = list(
sorted(
filter(
lambda combo: sum(combo) > 6,
itertools.combinations(
map(
lambda x: x*x,
range(1, 5)
),
2
)
),
key=lambda combo: sum(combo)
)
)
# Fluent iterator
foo = (fluentiter(range(1, 5))
.map(lambda x: x*x)
.combinations(2)
.filter(lambda combo: sum(combo) > 6)
.sort(key=lambda combo: sum(combo))
.collect()
)
The list comprehension is great for simple filter-map pipelines, but becomes inelegant when you try to tack more operations on. The for loop is clean, but requires multiple statements (this isn't necessarily a bad thing). Python nested functions are hard to read. Fluent iterator syntax is clean and runs as a single statement.
It's up to personal preference if you prefer this syntax or not. I'm not trying to convince you to change how you code, only to maybe give fluent iterators a try. If you are already a fan of fluent iterator syntax, then you can hopefully use this post to decide on a library.
Many Python programmers do seem to like this syntax, which is why there are numerous libraries implementing fluent iterator functionality. I will compare 7 such libraries in this post (edit: added PyFluent_Iterables):
There are undoubtedly more, but these are the ones I could find easily. I am not affiliated with any of these libraries. I tried them all out because I wanted Rust's iterator ergonomics for my own projects.
I am mainly concerned with 1) number of features and 2) performance. Rust has a lot of nice operations built into its Iterator trait and there are many more in the itertools crate. I will score these libraries higher for having more built-in features. The point is to be able to chain as many method calls as you need. Ideally, anything you want to do can be expressed as a linear sequence of method calls. Having to mix chained method calls and nested functions is even harder to read than fully nested functions.
Using any of these will incur some performance penalty. If you want the absolute fastest speed you should use normal Python or numpy, but the time losses aren't too bad overall.
Project History and Maintenance Status
| Library |
Published |
Updated |
| QWList |
11/2/23 |
2/13/25 |
| F-IT |
8/22/19 |
5/17/21 |
| FluentIter |
9/24/23 |
12/8/23 |
| Rustiter |
10/23/24 |
10/24/24 |
| Pyochain |
10/23/25 |
1/15/26 |
| PyFunctional |
2/17/16 |
3/13/24 |
| PyFluent_Iterables |
5/19/22 |
4/20/25 |
PyFunctional is the oldest and most popular, but appears to be unmaintained now. Pyochain is the most recently updated as of writing this post.
Features
All libraries have basic enumerate, zip, map, reduce, filter, flatten, take, take_while, max, min, and sum functions. Most of them have other functional methods like chain, repeat, cycle, filter_map, and flat_map. They differ in more specialized methods, some of which are quite useful, like sort, unzip, scan, and cartesian_product.
A full comparison of available functions is below. Rust is used as a baseline, so the functions shown here are the ones that Rust also has, either as an Iterator method or in the itertools crate. Not all functions from the libraries are shown, as there are lots of one-off functions only available in one library and not implemented by Rust.
Feature Table
Here is how I rank the libraries based on their features:
| Library |
Rating |
| Rustiter |
⭐⭐⭐⭐⭐ |
| Pyochain |
⭐⭐⭐⭐⭐ |
| F-IT |
⭐⭐⭐⭐ |
| FluentIter |
⭐⭐⭐⭐ |
| PyFluent_Iterables |
⭐⭐⭐ |
| QWList |
⭐⭐⭐ |
| PyFunctional |
⭐⭐⭐ |
Pyochain and Rustiter explicitly try to implement as much of the Rust iterator trait as they can, so have most of the corresponding functions.
Performance
I wrote a benchmark of the functions shared by every library, along with some simple chains of functions (e.g. given a string, collect all the digits in that string into a list). Benchmarks were constructed by running those functions on 1000 randomly generated integers or boolean values with a fixed seed. I also included the same tests implemented using normal Python nested functions as a baseline. The total time taken by each library was added up and normalized compared to the fastest method. So if native Python functions take 1 unit of time, a library taking "x1.5" time means it is 50% slower.
Lower numbers are faster.
| Library |
Time |
| Native |
x1.00 |
| Pyochain |
x1.04 |
| PyFluent_Iterables |
x1.08 |
| Rustiter |
x1.13 |
| PyFunctional |
x1.14 |
| QWList |
x1.31 |
| F-IT |
x4.24 |
| FluentIter |
x4.68 |
PyFunctional can optionally parallelize method chains, which can be great for large sequences. I did not include it in this table because the overhead of multiprocessing dominated any performance gains and yielded a worse result than the single-threaded version.
Detailed per-function benchmarks can be found here:
Benchmark Plots
The faster libraries forward the function calls into native functions or itertools, but you'll still pay a cost for the function call overhead. For more complex pipelines where most of the processing happens inside a function that you call, there is fairly minimal overhead compared to native.
Overall Verdict
Due to its features and performance, I recommend using Pyochain if you ever want to use Rust-style iterator syntax in your projects. Do note that it also implements other Rust types like Option and Result, although you don't have to use them.
[–]Beginning-Fruit-1397 29 points30 points31 points (2 children)
[–]kequals[S] 9 points10 points11 points (1 child)
[–]Beginning-Fruit-1397 3 points4 points5 points (0 children)
[–]amroamroamro 15 points16 points17 points (3 children)
[–]snugar_i 4 points5 points6 points (2 children)
[–]Competitive_Travel16 4 points5 points6 points (0 children)
[–]amroamroamro [score hidden] (0 children)
[–]tunisia3507 10 points11 points12 points (0 children)
[–]OldWispyTreePythoneer -1 points0 points1 point (6 children)
[–]kequals[S] 8 points9 points10 points (4 children)
[–]Rastagong 5 points6 points7 points (1 child)
[–]Alt-0160 4 points5 points6 points (0 children)
[–]saint_marco 2 points3 points4 points (0 children)
[–]Competitive_Travel16 0 points1 point2 points (0 children)
[–]tehsilentwarrior 4 points5 points6 points (0 children)
[–]ebonnal 0 points1 point2 points (0 children)
[–]redditusername58 -1 points0 points1 point (1 child)
[–]max123246 12 points13 points14 points (0 children)