Introduction
One of the basic limitation of unpacking is that you must know the length of the sequences you are unpacking in advance.
How to do it..
random_numbers = [0, 1, 5, 9, 17, 12, 7, 10, 3, 2] random_numbers_descending = sorted(random_numbers, reverse=True) print(f"Output \n*** {random_numbers_descending}")
Output
*** [17, 12, 10, 9, 7, 5, 3, 2, 1, 0]
If I now wanted to find out the largest and second largest from the numbers, we will get an exception "too many values to unpack".
print(f"Output \n*** Getting the largest and second largest") largest, second_largest = random_numbers_descending
Output
*** Getting the largest and second largest
--------------------------------------------------------------------------- ValueError Traceback (most recent call last) in 1 print(f"Output \n*** Getting the largest and second largest") ----> 2 largest, second_largest = random_numbers_descending ValueError: too many values to unpack (expected 2)
Python often rely on indexing and slicing. For example, when I want to extract the largest, second largest from a list of items below is how we can do.
largest = random_numbers_descending[0] print(f"Output \n*** Getting the largest - {largest}")
Output
*** Getting the largest - 17
second_largest = random_numbers_descending[1] print(f"Output \n*** Getting the second largest - {second_largest}")
Output
*** Getting the second largest - 12
rest_of_numbers = random_numbers_descending[2:] print(f"Output \n*** Getting the rest of numbers - {rest_of_numbers}")
Output
*** Getting the rest of numbers - [10, 9, 7, 5, 3, 2, 1, 0]
While this works, all of the indexing and slicing is visually noisy. In practice, it is error prone to divide the members of a sequence into various subsets this way.
To do it better, Python supports catch-all unpacking through a starred expression.
This starred syntax allows one part of the unpacking assignment to receive all values that do not match any other part of the unpacking pattern.
largest,second_largest, *rest_of_numbers = random_numbers_descending print(f"Output \n largest: {largest} \n second_largest:{second_largest} \n rest_of_numbers:{rest_of_numbers}")
Output
largest: 17 second_largest:12 rest_of_numbers:[10, 9, 7, 5, 3, 2, 1, 0]
How does the above code look? In a single line which is also easier to read we are able to acheive the output. A starred expression may appear in any position, so you can get the benefits of catch-all unpacking anytime you need to extract one slice
largest: 17 rest_of_numbers:[12, 10, 9, 7, 5, 3, 2, 1] smallest:0
*rest_of_numbers, second_smallest, smallest = random_numbers_descending print(f"Output \n rest_of_numbers:{rest_of_numbers} \n second_smallest: {second_smallest} \n smallest:{smallest}")
rest_of_numbers:[17, 12, 10, 9, 7, 5, 3, 2] second_smallest: 1 smallest:0
However, to unpack assignments that contain a starred expression, you must have at least one required part, or else you’ll get a SyntaxError. We can’t use a catch-all expression on its own.
*rest_of_numbers = random_numbers_descending
File "", line 1 *rest_of_numbers = random_numbers_descending ^ SyntaxError: starred assignment target must be in a list or tuple
We also cannot use multiple catch-all expressions in a single-level unpacking pattern. This is another important note to consider.
*rest_of_numbers, *more_smallest, smallest = random_numbers_descending
File "", line 1 *rest_of_numbers, *more_smallest, smallest = random_numbers_descending ^ SyntaxError: two starred expressions in assignment
But it is possible to use multiple starred expressions in an unpacking assignment statement, as long as they’re catch-alls for different parts of the multilevel structure being unpacked.
player_grandslame_and_atptitles = { 'Federer': (20, 103), 'Nadal': (20,84),} ((player1, (grandslam1, *atptitles1)), (player2, (grandslam2, *atptitles2))) = player_grandslame_and_atptitles.items() print(f'Output \nPlayer - {player1} Have acheived {grandslam1} grandslams and , {atptitles1} atp tour titles') print(f'Player - {player2} Have acheived {grandslam2} grandslams and , {atptitles2} atp tour titles')
Output
Player - Federer Have acheived 20 grandslams and , [103] atp tour titles Player - Nadal Have acheived 20 grandslams and , [84] atp tour titles
Starred expressions become list instances in all cases. If there are no leftover items from the sequence being unpacked, the catch-all part will be an empty list. This is especially useful when you are processing a sequence that you know in advance has at least N elements.
random_numbers = [0, 1] first, second, *rest = random_numbers print(f"Output \n{first, second, rest}")
Output
(0, 1, [])