Search code examples
pythoniterable-unpackingpep

Why does unpacking give a list instead of a tuple in Python?


This is really strange to me, because by default I thought unpacking gives tuples.

In my case I want to use the prefix keys for caching, so a tuple is preferred.

# The r.h.s is a tuple, equivalent to (True, True, 100)
*prefix, seed = ml_logger.get_parameters("Args.attn", "Args.memory_gate", "Args.seed")
assert type(prefix) is list

But I thought unpacking would return a tuple instead.

Here is the relevant PEP: https://www.python.org/dev/peps/pep-3132/

-- Update --

Given the comment and answers bellow, specifically I was expecting the unpacking to give a tuple because in function arguments a spread arg is always a tuple instead of a list.

As Jason pointed out, during unpacking one would not be able to know the length of the result ahead of time, so implementation-wise the catch-all has to start as a list for dynamic appends. Converting it to a list is a waste of effort the majority of the time.

Semantically, I would prefer to have a tuple for consistency.


Solution

  • This issue was mentioned in that PEP (PEP 3132):

    After a short discussion on the python-3000 list [1], the PEP was accepted by Guido in its current form. Possible changes discussed were: [...]

    • Try to give the starred target the same type as the source iterable, for example, b in a, *b = 'hello' would be assigned the string 'ello'. This may seem nice, but is impossible to get right consistently with all iterables.

    • Make the starred target a tuple instead of a list. This would be consistent with a function's *args, but make further processing of the result harder.

    But as you can see, these features currently are not implemented:

    In [1]: a, *b, c = 'Hello!'
    In [2]: print(a, b, c)
    H ['e', 'l', 'l', 'o'] !
    

    Maybe, mutable lists are more appropriate for this type of unpacking.