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:

1 Like

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:

3 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.

3 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.

4 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:

3 Likes

Tysm for the incredibly detailed and thoughtful explanation! That absolutely cleared it up for me.

I really got tripped up by the syntax. I was treating the parentheses as the key indicator, and totally missed the significance of the comma itself. Your examples made it click instantly.

Really appreciate you taking the time this kind of clarity is gold when you’re stuck on something that feels just out of reach. :slightly_smiling_face:

Hi @Priuset :wave:

While I appreciate the positive feedback and I am glad that you found my explanation helpful, I am less appreciative of you embedding a spam link in the post quote. I’ve removed it.

Please don’t do that again here on the forum. Thanks! :slightly_smiling_face:

2 Likes