Using one of the latest Ruff linting rules (from refurb), I recently cleaned a codebase where I had code looking like this (dump contains formatting code):
# Before
"".join(dump(*t) for t in some_iterable)
# After
from itertools import starmap
"".join(starmap(dump, some_iterable))
After finishing this cleanup, I grepped my codebase and realized I had many other occurrences of a similar construct:
[SomeClass(**kw) for kw in some_iterable]
The use case I have is basically an alternate constructor for a class where I collect dicts that will be used as keyword parameters (I can expand on this but I want to keep this post short). This made me think that I could have used an hypothetic itertools.doublestarmap (or a better name):
To be honest, I’ve always felt that having this in itertools is the wrong level of abstraction, or rather, the wrong way to split the task. I’d rather have adapters equivalent to
which would instead live in functools, and which would then allow
''.join(map(starred(dump), some_iterable))
''.join(starred(dump)(t) for t in some_iterable)
''.join(map(doublestarred(dump), some_iterable))
''.join(doublestarred(dump)(t) for t in some_iterable)
# etc.
Although it might not be possible to get as good performance that way…
But yeah, from a functional programming perspective, that sort of adapter seems a lot more practical than custom-purpose tweaked re-implementations of the map logic.
None of them pass arguments by keyword. There is no keyword argument analogue of zip(). This looks like more niche application. You already have several ways to write this using existing syntax, so I think there is very little chance of introducing new functions in the stdlib.
Can you explain what problem this solves? In your first example I would prefer the version without starmap, as it’s shorter and doesn’t require the reader to know what that function does. Is there some reason I’m missing why it’s better to avoid the generator expression?
seems just as simple to me. Only the function names swapped and one of the *s moved. The choice is largely arbitrary, and the fact that there is a choice is really just a consequence of having functions that accept multiple parameters with “(un)packing” being a coherent concept. (That’s typical of most languages, including the Lisp family if you squint a bit, but not e.g. the Haskell family as I understand it.)
I find the two forms fairly equivalent (one could also debate whether map is more readable than the equivalent comprehension).
I started rewriting some code using starmap instead of a comprehension for the small performance benefit, where it matters for me.
If doublestarmap had been part of the standard library, I would probably do the same, for the same reason.