Potential for expansion of packing to list syntax explanation

The Python instructions for the Locomotive Engineer exercise includes the following:

# If the * operator is used on the left side of "=" the result is a list
>>> *combined_fruits_too, = *fruits, *more_fruits

I tried using this technique in the exercise, but initially missed the trailing comma on the left side of the assignment, and didn’t understand why I was getting a Pylance error. Some googling eventually showed me that it was necessary (Unpacking in Python: Beyond Parallel Assignment to be precise), but even that article doesn’t really explain why it’s necessary, only that it is.

I would suggest that when the * operator’s utility on the left side is pointed out that the trailing comma be explicitly pointed out and the reason for its necessity be explained.

If I knew the explanation, I’d offer a suggestion as to what might be included. :slight_smile:

Hi @chivalry :wave:

What an excellent question!
This is a tad tricky to explain, so forgive the long post. :sweat_smile:


It is a bit of a foot-gun, but in Python it is not the () that signify a tuple., its the comma between elements.

>>> my_container = "one", "two", "three"
>>> type(my_container)
<class 'tuple'>

The () are used in cases of ambiguity or where you need to make sure that anyone reading the code understands that what you are using is a tuple. But to the Python parser, it is the comma that is significant.

Small problem: if you want to create a tuple with a single element, you have to use a trailing comma:

>>> one_element = "the only one", #<-- note trailing comma
>>> type(one_element)
<class 'tuple'>

A paragraph from the article you linked hints at why the comma is necessary on the left-hand side in the example we give:

For historical reasons, Python developers used to call this tuple unpacking . However, since this feature has been generalized to all kind of iterable, a more accurate term would be iterable unpacking and that’s what we’ll call it in this tutorial.

On the left-hand side, “tuple unpacking” is still what is going on under the covers (a tuple is made from the right-hand elements and then assigned to the elements of the tuple made on the left-hand side.). But thanks to PEP3132, there is added functionality for packing “remainders” of the assignment into a list:

>>> a, *b, c = range(5)
>>> a
0
>>> c
4
>>> b
[1, 2, 3]

Except in our example, its all remainder, because of the *:

>>> *combined_fruits_too, = *fruits, *more_fruits
>>> combined_fruits_too
['apple', 'banana', 'cherry', 'orange', 'kiwi', 'melon', 'mango']

So our example could be called a special case of that remainders packing. We make a tuple with a single element and flag it as a remainder. The result is that everything goes into the remainders list as outlined in PEP3132.

But we have to respect the tuple unpacking syntax, and make the target a tuple with a single element. The left-hand number of elements has to match the right-hand number of elements.

It is a bit strange, but you can also write it this way:

>>> [*combined_fruits_too] = *fruits, *more_fruits
>>> combined_fruits_too
['apple', 'banana', 'cherry', 'orange', 'kiwi', 'melon', 'mango']

Which makes it even more obscure to figure out what happened.
It could be argued that a clearer way to write the same thing would be to:

>>> combined_fruits = *fruits, *more_fruits
>>> combined_fruits_too = [*combined_fruits]
>>> combined_fruits_too
['apple', 'banana', 'cherry', 'orange', 'kiwi', 'melon', 'mango']

or just

>>> combined_fruits = [*fruits, *more_fruits]
>>> combined_fruits
['apple', 'banana', 'cherry', 'orange', 'kiwi', 'melon', 'mango']

But then it wouldn’t be a magic shortcut. :wink:

2 Likes

Going to add here that adding all that to the exercise would be way way too much. So here we apply the lie to children principal.

2 Likes

@BethanyG, ty so much for the detailed explanation. I’ll probably have to read it a couple more times to fully grok the concept. :slight_smile:

Yes, this is “way way too much” for an exercise readme, not sure how to help minimize a repetition of my own misunderstanding for future students, but perhaps something as short as, “Note the trailing comma on the left, indicating that we’re assigning the entire remainder to a single-item tuple,” perhaps including a link to further explanation.

3 Likes

Hi @chivalry :wave:

I took a run at some small changes. Have a look at PR 3826, which changes both the intro and the about.md slightly.

Helpful or not? Let me know. :smile:

2 Likes