Today we’ll be talking about extended sequence unpacking.
We’ll see how to assign the elements of a sequence to multiple variables in one go. We’ll have a look at basic sequence assignment as a starting point.
Basic Sequence Assignment
You can assign the elements of a sequence to multiple variables. The condition that must be met is that the number of the elements must be equal to the number of the variables:
In case of tuple assignment, the parentheses may be skipped:
>>> a, b, c, d = 1, 2, 3, 4
>>> a
1
>>> b
2
>>> c
3
>>> d
4
If there are different numbers on both sides of the assignment operator, we get an error:
>>> x, y, z = 1, 2, 3, 4, 5
Traceback (most recent call last):
File "<stdin>", line 1, in <module>
ValueError: too many values to unpack (expected 3)
Here are some more examples of sequence assignments:
– tuple assignment with parentheses:
>>> a, b, c = ('one', 'two', 'three')
>>> a
'one'
>>> b
'two'
>>> c
'three'
– list assignment:
>>> p, q, r, s, t = [True, True, False, False, True]
>>> p, t
(True, True)
– string assignment:
>>> letter1, letter2, letter3, letter4, letter5 = 'hello'
>>> letter1, letter3, letter5
('h', 'l', 'o')
– another string assignment:
>>> animal = 'cat'
>>> a, b, c = animal
>>> a
'c'
>>> b
'a'
>>> c
't'
We can assign to nested sequences as well:
>>> (a, b, c), d = 'dog', 'cat'
>>> a, b, c, d
('d', 'o', 'g', 'cat')
>>> a, (b, c, d), e = 'dog', 'cat', 'mouse'
>>> a, b, c, d, e
('dog', 'c', 'a', 't', 'mouse')
It also works with ranges:
>>> a, b, c, d = range(4)
>>> a, b, c, d
(0, 1, 2, 3)
We can also assign to loop variables provided the patterns match:
>>> for (a, (b, c)) in [(1, (2, 3)), (4, (5, 6))]:
... print(f"a = {a} b = {b} c = {c}")
...
a = 1 b = 2 c = 3
a = 4 b = 5 c = 6
Extended Sequence Unpacking
And now, after this lengthy introduction, let’s have a look at the actual topic of this article, extended sequence unpacking.
If we use the star operator, it’s possible to have different numbers of elements on each side of the assignment operator. The starred variable will be assigned a list with all the leftovers that were not assigned to the other variables. There may be only one starred variable:
Here’s a simple example:
>>> animals = ['monkey', 'snake', 'rhino', 'frog', 'slug']
>>> first, *rest = animals
>>> first, rest
('monkey', ['snake', 'rhino', 'frog', 'slug'])
or the other way around:
>>> *most, last = animals
>>> most, last
(['monkey', 'snake', 'rhino', 'frog'], 'slug')
We can even assign the elements from the middle:
>>> first, *middle3, last = animals
>>> first, middle3, last
('monkey', ['snake', 'rhino', 'frog'], 'slug')
or:
>>> first, second, *next2, last = animals
>>> first, second, next2, last
('monkey', 'snake', ['rhino', 'frog'], 'slug')
In all the above examples we were using the animals list, but it may be any sequence:
– tuples:
>>> grades = ('A', 'B', 'C', 'D', 'E', 'F')
>>> best_grade, *other_grades, worst_grade = grades
>>> best_grade, other_grades, worst_grade
('A', ['B', 'C', 'D', 'E'], 'F')
– strings:
>>> word = 'unbelievable'
>>> first, *middle, last_but_one, last = word
>>> first, last_but_one, last
('u', 'l', 'e')
>>> middle
['n', 'b', 'e', 'l', 'i', 'e', 'v', 'a', 'b']
– ranges:
>>> a, *b, c, d = range(20)
>>> a, b, c, d
(0, [1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16, 17], 18, 19)
Most of this can be achieved with slicing. The difference is that in slicing you obtain an object of the same type. So, if you slice a string, you obtain a string, if you slice a list, you obtain a list, and the same with tuples. Using extended sequence unpacking you always obtain a list.
Here’s how you can use it in loops:
>>> for a, *b, c in ("&music&", "&art&", "&science&"):
... print(b)
...
['m', 'u', 's', 'i', 'c']
['a', 'r', 't']
['s', 'c', 'i', 'e', 'n', 'c', 'e']
Some Special Cases
Here are some special cases to note:
1) If the number of elements to unpack is the same as the number of variables to assign to, the starred variable will be assigned a list anyway:
>>> name = "Jenny"
>>> a, b, *c, d, e = name
>>> a, b, c, d, e
('J', 'e', ['n'], 'n', 'y')
2) If there is nothing left to collect in the list with the leftovers, an empty list is assigned to the starred variable:
>>> a, b, *c, d, e, f = name
>>> a, b, c, d, e, f
('J', 'e', [], 'n', 'n', 'y')
3) The star assignment target on the left must be in a list or tuple. Otherwise we get an error:
>>> nums = [1, 2, 3, 4, 5]
>>> *a = nums
File "<stdin>", line 1
SyntaxError: starred assignment target must be in a list or tuple
Let it be in a list:
>>> [*a] = nums
>>> a
[1, 2, 3, 4, 5]
Now let it be in a one-element tuple:
>>> (*a,) = nums
>>> a
[1, 2, 3, 4, 5]
The parentheses may be skipped, but not the comma:
>>> *a, = nums
>>> a
[1, 2, 3, 4, 5]
As mentioned before, we can achieve practically the same using indexing and slicing, but extended sequence unpacking is usually simpler to code, so why not use it if it’s out there for us to use?
Here you can watch the video version: