Nice and clean iterator patterns for python
Project description
FluentIter
Nicer iterator patterns for Python! Chain map, filter, zip, unzip, cycle, skip and scan like there is no tomorrow.
Install with
pip install fluentiter
TLDR:
With fluentiter you can do this
lines = iterator(haystack_csv.split("\n"))
header = [x for x in lines.next().split(",") if x.strip(" ")]
needle = (
lines.map(lambda line: line.split(","))
.map(lambda values: {k: v for k, v in zip(header, values)})
.filter(lambda value_dct: value_dct["material"] != "hay")
.find(lambda value_dct: value_dct["type"] == "needle")
)
instead of this
lines = iter(some_csv.split("\n"))
header = [x for x in next(lines).split(",") if x.strip()]
needle = next(
filter(
lambda value_dct: value_dct["material"] != "hay",
filter(
lambda value_dct: value_dct["type"] == "needle",
map(
lambda values: {k: v for k, v in zip(header, values)},
map(lambda line: line.split(","), lines),
),
),
)
)
See the this example for a more complete version.
Motivation
To me Pythons iterator functions (i.e. map, filter, zip, etc.) always seemed backward.
While generator expressions and list/dict/set comprehensions give you nice left-to-right readability,
map and filter force you to read "inside-out":
[func(x) for x in something if x == y]
map(func, filter(lambda x: x == y, something))
This is not an issue for short an simple statements, as they can be written concisely using expressions, but chaining multiple operations, will result in either many intermediate variable assignements or serious spaghettification.
How to use
Any Iterable can be turned into a FluentIterator by just passing it to the iterator function:
from fluentiter import iterator
bugs = ["john", "paul", "ringo", "george"]
fluent = iterator(bugs)
# you can now do
# fluent.map(...).filter(...).cycle(...).scan(...) and so on
The FluentIterator provides a rich set of methods you can call and chain together to your liking:
Of course there are the classic map, filter, and reduce functions, but also some more really useful features like cycle to repeat an iterator forever, find to find an element, into to collect into containers, or apply to transform and continue chaining. There's even partition to turn your iterator into two.
In total FluentIterator provides 38 methods to compose beautiful and easy to follow iteration
patterns.
Check the API docs to see them all.
Features
- 38 cool fresh iterator methods
- 100% Type annotated e.g.
iterator(["foo", "bar"]).map(len).to_list()gets correctly inferred aslist[int] - 100% Test coverage
- 0 dependencies outside the Python standard library[^0]
Contributing
The simplest way to contribute is to open an issue. If you would like to see some feature implemented or found a bug, head over to the issues section.
Developing
- Clone the repository
- Install uv
- Install development dependencies with
uv sync --all-extras
Guidelines
Your PR should include relevant tests for the changes you are contributing and be fully type annotated. If you are unsure or stuck, please open the PR anyway and we can work it out together.
By opening a PR you agree to your code becoming part of the fluentiter package and being published under fluentiters license.
Running formatting, tests, and linting
- Formatting:
uv run poe format - Linting:
uv run poe lint - Tests:
uv run poe test
All of the above: uv run poe all
Viewing the coverage report
Running uv run coverage html will create a file htmlcov/index.html you can open to view the test coverage report
Changelog
- 1.0.0
- initial release
- 1.0.1
- fixed errors int readme
- 1.1.0
- add
into(...)method
- add
- 1.1.1
- add
py.typedmarker file
- add
- 1.2.0
- add
tumbling_windowfunction - fix mypy inference on
.flatten()and.flat_map - fix
more_itertoolsbeing installed automatically
- add
- 1.2.1
- fix parameter key not being respected in
.minand.max
- fix parameter key not being respected in
- 1.3.0
- add
apply(...)method - drop support for Python 3.9
- add
Special Thanks
Thank you to all Rust maintainers for creating the Iterator trait, which served as the main source of inspiration.
[^0]: Some functions may require the more extra which has more-itertools as a dependency
Project details
Download files
Download the file for your platform. If you're not sure which to choose, learn more about installing packages.
Source Distribution
Built Distribution
Filter files by name, interpreter, ABI, and platform.
If you're not sure about the file name format, learn more about wheel file names.
Copy a direct link to the current filters
File details
Details for the file fluentiter-1.3.0.tar.gz.
File metadata
- Download URL: fluentiter-1.3.0.tar.gz
- Upload date:
- Size: 12.9 kB
- Tags: Source
- Uploaded using Trusted Publishing? Yes
- Uploaded via: twine/6.1.0 CPython/3.13.7
File hashes
| Algorithm | Hash digest | |
|---|---|---|
| SHA256 |
579b3a58bc3736a31f1f85406b72c9bee80f2fcdc5584a069f9c7355d58adfc2
|
|
| MD5 |
d0b2b80818852a6f7d63b616c40c9a34
|
|
| BLAKE2b-256 |
092db227769700c64c6e431abfb559baa9ff630f02c5556a955771302de32a8c
|
Provenance
The following attestation bundles were made for fluentiter-1.3.0.tar.gz:
Publisher:
publish-pypi.yml on damiondoesthings/fluentiter
-
Statement:
-
Statement type:
https://in-toto.io/Statement/v1 -
Predicate type:
https://docs.pypi.org/attestations/publish/v1 -
Subject name:
fluentiter-1.3.0.tar.gz -
Subject digest:
579b3a58bc3736a31f1f85406b72c9bee80f2fcdc5584a069f9c7355d58adfc2 - Sigstore transparency entry: 1145838800
- Sigstore integration time:
-
Permalink:
damiondoesthings/fluentiter@34c7c023513d26077fdf637ffcb717a92e43c0f4 -
Branch / Tag:
refs/heads/main - Owner: https://github.com/damiondoesthings
-
Access:
public
-
Token Issuer:
https://token.actions.githubusercontent.com -
Runner Environment:
github-hosted -
Publication workflow:
publish-pypi.yml@34c7c023513d26077fdf637ffcb717a92e43c0f4 -
Trigger Event:
workflow_dispatch
-
Statement type:
File details
Details for the file fluentiter-1.3.0-py3-none-any.whl.
File metadata
- Download URL: fluentiter-1.3.0-py3-none-any.whl
- Upload date:
- Size: 14.2 kB
- Tags: Python 3
- Uploaded using Trusted Publishing? Yes
- Uploaded via: twine/6.1.0 CPython/3.13.7
File hashes
| Algorithm | Hash digest | |
|---|---|---|
| SHA256 |
7d3aa07f3c4c182feb4c73c734653b60d3d9cfb09257c2d0557776fda290c70e
|
|
| MD5 |
8c041f590930de732f0bbe9e418b2501
|
|
| BLAKE2b-256 |
613dd2b8337f9053006e099117c1ee1f85199d3193091a1c4d88fce126b78c54
|
Provenance
The following attestation bundles were made for fluentiter-1.3.0-py3-none-any.whl:
Publisher:
publish-pypi.yml on damiondoesthings/fluentiter
-
Statement:
-
Statement type:
https://in-toto.io/Statement/v1 -
Predicate type:
https://docs.pypi.org/attestations/publish/v1 -
Subject name:
fluentiter-1.3.0-py3-none-any.whl -
Subject digest:
7d3aa07f3c4c182feb4c73c734653b60d3d9cfb09257c2d0557776fda290c70e - Sigstore transparency entry: 1145838842
- Sigstore integration time:
-
Permalink:
damiondoesthings/fluentiter@34c7c023513d26077fdf637ffcb717a92e43c0f4 -
Branch / Tag:
refs/heads/main - Owner: https://github.com/damiondoesthings
-
Access:
public
-
Token Issuer:
https://token.actions.githubusercontent.com -
Runner Environment:
github-hosted -
Publication workflow:
publish-pypi.yml@34c7c023513d26077fdf637ffcb717a92e43c0f4 -
Trigger Event:
workflow_dispatch
-
Statement type: