PEP-3132 (extended iterable unpacking) specifies the “starred” expression as (emphasis mine):
a subexpression that will be assigned a list of all items from the iterable being unpacked that are not assigned to any of the mandatory expressions, or an empty list if there are no such items.
I would like to propose a new __unpack__(before: int, after: int) → tuple magic method that allows an iterable class to customize how it is unpacked. It takes two arguments, the number of mandatory expressions before and after the starred expression and should return a tuple of before + 1 + after elements. With this, an extended iterable unpacking expressions such as
`a, b, *mid, c, d, e = iterable`
would become syntax sugar for a, b, mid, c, d, e = iterable.__unpack__(2, 3)
Then for example a range.__unpack__ implementation would return mid as a range instead of a list. Likewise for Numpy arrays, Pandas series and other similar libraries.
I’m not sure I understand what the value would be here. If I’m unpacking in Iterable that exists (i.e. list, tuple, etc) it’s repackaging existing objects. If it’s with one that doesn’t exists (something built on the fly like a range, or a generator) it still needs to iterate over that Iterable and create them.
I see a huge problem with anyone wanting to use this in combination with type hints (except for naming being similar (obviously) to typing).
How would one be able to hint the return type of __unpack__? I suppose tuple[T, .. ] would work, but being able to specify that we return a tuple[T*(before+after+1)] could be useful.
Also, how could this affect typing specific features relying on the syntax, mainly the unpacking of TypeVarTuples?
Still, after all these questions, it’s a likely +1 for me, being able to introduce such ‘context-aware’ special methods could help users to create more stuff they want implemented.
Yeah precise return type doesn’t seem possible, at least with today’s typing. E.g. for range best i can come up with is tuple[int | range, ...].
Ironically, the reason I came up with this idea in the first place was typing-related. I have a use case for a class that I’d like it to be iterable but not unpackable. If I could define __iter__ normally and set __unpack__ = None, a type checker would be able to flag unpacking without disallowing iteration.
EDIT: actually for this use case the opposite solution would be just as acceptable: if the type checker does not flag an unpacking of this iterable as error, I would need a way to ensure that it never never fails at runtime (without try/except). As of now there’s no way (afaict) to define an iterable for which both x, y = iterable and x, y, z = iterable succeed.
Both acceptable solutions (statically disallow unpacking or guarantee it never fails at runtime) would be feasible if there was an __unpack__ method.