Hi @chivalry
What an excellent question!
This is a tad tricky to explain, so forgive the long post.
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.