How do you override the result of unpacking syntax *obj
and **obj
?
For example, can you somehow create an object thing
which behaves like this:
>>> [*thing]
['a', 'b', 'c']
>>> [x for x in thing]
['d', 'e', 'f']
>>> {**thing}
{'hello world': 'I am a potato!!'}
Note: the iteration via __iter__
("for x in thing") returns different elements from the *splat unpack.
I had a look inoperator.mul
and operator.pow
, but those functions only concern usages with two operands, like a*b
and a**b
, and seem unrelated to splat operations.
*
iterates over an object and uses its elements as arguments.**
iterates over an object'skeys
and uses__getitem__
(equivalent to bracket notation) to fetch key-value pairs. To customize*
, simply make your object iterable, and to customize**
, make your object a mapping:If you want
*
and**
to do something besides what's described above, you can't. I don't have a documentation reference for that statement (since it's easier to find documentation for "you can do this" than "you can't do this"), but I have a source quote. The bytecode interpreter loop inPyEval_EvalFrameEx
callsext_do_call
to implement function calls with*
or**
arguments.ext_do_call
contains the following code:which, if the
**
argument is not a dict, creates a dict and performs an ordinaryupdate
to initialize it from the keyword arguments (except thatPyDict_Update
won't accept a list of key-value pairs). Thus, you can't customize**
separately from implementing the mapping protocol.Similarly, for
*
arguments,ext_do_call
performswhich is equivalent to
tuple(args)
. Thus, you can't customize*
separately from ordinary iteration.It'd be horribly confusing if
f(*thing)
andf(*iter(thing))
did different things. In any case,*
and**
are part of the function call syntax, not separate operators, so customizing them (if possible) would be the callable's job, not the argument's. I suppose there could be use cases for allowing the callable to customize them, perhaps to passdict
subclasses likedefaultdict
through...