Search code examples
pythonargsiterable-unpacking

python: when can I unpack a generator?


How does it work under the hood? I don't understand the reason for the errors below:

>>> def f():
...     yield 1,2
...     yield 3,4
...
>>> *f()
  File "<stdin>", line 1
    *f()
    ^
SyntaxError: invalid syntax
>>> zip(*f())
[(1, 3), (2, 4)]
>>> zip(f())
[((1, 2),), ((3, 4),)]
>>> *args = *f()
File "<stdin>", line 1
  *args = *f()
    ^
SyntaxError: invalid syntax

Solution

  • The *iterable syntax is only supported in an argument list of a function call (and in function definitions).

    In Python 3.x, you can also use it on the left-hand side of an assignment, like this:

    [*args] = [1, 2, 3]
    

    For this particular example, this is actually equivalent to

    args = [1, 2, 3]
    

    The version using argument unpacking creates a shallow copy of the list, but when using a list literal on the right-hand side of the assignment, this difference is not observable.

    The syntax is more commonly used with multiple assignment targets on the left-hand side:

    head, *tail = some_list
    

    This assigns the first element of some_list to head and the remainder (potentially empty) to tail. It will throw a ValueError if some_list is empty, since then there's nothing to assign to head.

    While you can use the argument unpacking syntax to create a shallow copy of a list, I wouldn't recommend it for that purpose, since it kind of hides your intention. So instead of

    [*a] = some_list
    

    I'd write

    a = some_list.copy()
    

    It's slightly longer, but the intention is much clearer.