Introduction
Unpacking is the process of extracting values from a sequence/iterable and assigning them to multiple variables using only a single line of code.
It is assumed that the reader is familiar with Python lists, dictionaries, tuples and the built-in function zip.
A picture is worth a thousands words. In software, we have a similar concept: a carefully selected code snippet is worth a thousand words. This blog post will use the code snippet approach.
Not Using Asterisks
Example 1.A: multiple assignment using a list
>>> a, b, c = [1, 2, 3] >>> a 1 >>> b 2 >>> c 3
Example 1.B: multiple assignment using any iterable
Previous example used a list. This example, uses a string. It illustrate that unpacking works with any iterable.
Code
>>> d, e, f = '456' >>> d '4' >>> e '5' >>> f '6'
Example 1.C: multiple assignment using a dictionary
Example 1.C.1: just the dictionary keys
Code
>>> x, y = {'g': 7, 'h': 8} >>> x 'g' >>> y 'h'
Notice that the above unpacking retrievs only the keys of the dictionary.
Example 1.C.2: the dictionary key and associated value together
For the details on the items() method of a dictionary, click here.
Code
>>> d = {'a': 1, 'b': 2, 'c': 3} >>> x, y, z = d.items() >>> x ('a', 1) >>> x[0] 'a' >>> x[1] 1 >>> y ('b', 2) >>> z ('c', 3)
Single Asterisk (*)
Example 2: single asterisk to unpack a list
Example 2.A: single asterisk to unpack a list into the [ last | first ] assignment variable
Example 2.A.1: single asterisk to unpack a list into the last assignment variable
>>> a, *b = [1, 2, 3] >>> a 1 >>> b [2, 3]
Example 2.A.2: single asterisk to unpack a list into the first assignment variable
>>> *c, d = [7, 8, 9] >>> c [7, 8] >>> d 9
Example 2.B: single asterisk to unpack a list into the middle assignment variable
Example 2.B.1: single asterisk to unpack a list into the middle assignment variable - Simple
>>> e, *f, g = [10, 11, 12, 13] >>> e 10 >>> f [11, 12] >>> g 13
Example 2.B.2: single asterisk to unpack a list into the middle assignment variable - Complex
Code
>>> h, i, *j, k, l = [14, 15, 16, 17, 18, 19] >>> h 14 >>> i 15 >>> j [16, 17] >>> k 18 >>> l 19
Warning: putting too many variable initializations on the same line can cause readability issues. The above code snippet demonstrates this. One has to read the code carefully to determine what gets saved into "*j".
Example 2.C: single asterisk to unpack a list within a list
Example 2.C.1: single asterisk to unpack a list within a list - Once
>>> some_numbers = [1, 2, 3] >>> more_numbers = [*some_numbers, 4, 5] >>> more_numbers [1, 2, 3, 4, 5]
Example 2.C.2: single asterisk to unpack a list within a list - Multiple Times
>>> some_numbers = [1, 2, 3] >>> some_other_numbers = [4, 5, 6] >>> [*some_numbers, *some_other_numbers, 7, 8] [1, 2, 3, 4, 5, 6, 7, 8]
Example 2.D: combine built-in function zip with single asterisk unpacking
Example 2.D.1: extract elements of a list within a list into separate lists
>>> some_list_of_lists = [ ... [1, 'a'], ... [2, 'b'], ... [3, 'c'] ... ] >>> numbers, letters = zip(*some_list_of_lists) >>> numbers (1, 2, 3) >>> letters ('a', 'b', 'c')
Example 2.D.2: transpose a list of lists
>>> some_list_of_lists = [ ... [1, 2, 3], ... [4, 5, 6], ... [7, 8, 9] ... ] >>> list(zip(*some_list_of_lists)) [(1, 4, 7), (2, 5, 8), (3, 6, 9)]
Example 3: single asterisk for unpacking a list into positional arguments of a function (*args)
>>> def print_d(a, b, c): ... print(f"a = {a}") ... print(f"b = {b}") ... print(f"c = {c}") ... >>> d = (1, 2, 3) >>> print_d(*d) a = 1 b = 2 c = 3
Example 4: single asterisk gives arbitrary number of positional arguments to a function (*args)
>>> def sum_arbitrary_number_of_arguments(*args): ... result = 0 ... for arg in args: ... result+=arg ... return result ... >>> sum_arbitrary_number_of_arguments(1+2+3) 6 >>> sum_arbitrary_number_of_arguments(1+2+3+4) 10
Double Asterisk (**)
Example 5: double asterisk to unpack dictionary into keyword arguments
Example 5.A: double asterisk to unpack dictionary into keyword arguments - Simple
>>> full_name = {'last_name': "Flintstone", 'first_name': "Fred", 'middle_name': "Bedrock"} >>> "{last_name}-{first_name}-{middle_name}".format(**full_name) 'Flintstone-Fred-Bedrock'
Example 5.B: double asterisk to unpack dictionary into keyword arguments that match the function parameters (**kwargs)
>>> def print_d(a, b, c): ... print(f"a = {a}") ... print(f"b = {b}") ... print(f"c = {c}") ... >>> d = {'a': 1, 'b': 2, 'c': 3} >>> print_d(**d) a = 1 b = 2 c = 3
Example 6: merging dictionaries (double asterisk used multiple times)
Example 6.A: double asterisk for unpacking dictionaries in order to merge them
>>> d_1 = {'a': 1, 'b': 2} >>> d_2 = {'c': 3, 'd': 4 } >>> merged_dict = {**d_1, **d_2} >>> merged_dict {'a': 1, 'b': 2, 'c': 3, 'd': 4}
Example 6.B: double asterisk for unpacking dictionaries in order to merge them and override as needed
Code
>>> d_1 = {'a': 1, 'b': 2} >>> d_2 = {'b': 99, 'c': 3, 'd': 4 } >>> merged_dict = {**d_1, **d_2} >>> merged_dict {'a': 1, 'b': 99, 'c': 3, 'd': 4}
Notice how the final value of b is 99 even though its original value was 2.
Note
The above is used to demonstrate dictionary unpacking. The more Pythonic way to merge dictionaries is to use the vertical bar (|) operator.
merged_dict = d_1 | d_2
Example 7: double asterisk gives an arbitrary number of keyworded inputs to a function (**kwargs)
>>> def specialized_print(**kwargs): ... for x,y in kwargs.items(): ... print(f'{x} has a value of {y}') ... >>> specialized_print(a=1, b=2, c=3) a has a value of 1 b has a value of 2 c has a value of 3 >>> specialized_print(a=1, b=2, c=3, d=4) a has a value of 1 b has a value of 2 c has a value of 3 d has a value of 4
Slicing
Another way to achieve the same outcomes as unpacking is to use slicing. Let's redo "Example 2.B.2" using slicing.
Code
>>> some_list = [14, 15, 16, 17, 18, 19] >>> # get first value >>> some_list[0] 14 >>> # get second value >>> some_list[1] 15 >>> # get from third value to the end but leave out the last two >>> some_list[2:-2] [16, 17] >>> # get second to last value >>> some_list[-2] 18 >>> # get last value >>> some_list[-1] 19
Notice how much more verbose the slicing syntax is. In addition, thought needs to be given to the appropriate value(s) for indexing.
However, comparing unpacking and slicing is not helpful because their application will depend on the use case. It is included for the sake of completeness because people will often ask about slicing when the topic of unpacking is brought up.
Summary
Unpacking besides assigning variables enables the specification of the size and shape of the iterable.