MicroPython Unpacking Operators

Contents

Introduction

Operators are an essential part of just about any computing language ever devised and MicroPython is of no exception. The MicroPython operators can be broadly classified into eight groups:

  1. Arithmetic
    - , + , * , / , % , ** , //
  2. Assignment
    = , augmented assignments e.g. +=
  3. Comparison
    == , != , > , < , >= , <=
  4. Logical
    and , or , not
  5. Identity
    is , is not
  6. Membership
    in , not in
  7. Bitwise
    & , ! , ^ , ~ , << , >>
  8. Unpacking Operators
    * , **

What is Meant by Unpacking?

Simply put, unpacking is the extraction elements from collection datatypes an iterable e.g. string, list, tuple, set, range(), etc. This can be done by manual assignment or using an unpacking operator. Consider some simple examples of the former:


Example 1
a, b, c = range(3)
print('a =',a) ⇒ a = 0 
print('b =',b) ⇒ b = 1
print('c =',c) ⇒ c = 2

Example 2
L = [0, 1, 2]
a, b, c = L
print('a =',a) ⇒ a = 0 
print('b =',b) ⇒ b = 1
print('c =',c) ⇒ c = 2

Example 3
L = [0, 1, 2]
a, b = L
print('a =',a)
print('b =',b)

Traceback (most recent call last):
  File "<stdin>", line 2, in <module>
ValueError: too many values to unpack (expected 2)
          

Note that in Example 3 an error occurred because there were three values to unpack but only two variables supplied to receive the values.

Asterisk (*) Unpacking Operator

The asterisk (*) operator is used to unpack any elements of an iterable that haven't already been unpacked and assigned. It is a unary operator i.e. is applied only to a single variable and will return a list (or tuple). This is best shown by examples.


Example 1
first, second, *theRest = 'Hello'
print('first =', first)
print('second =', second)
print('theRest =', theRest)

Output:
first = H
second = e
theRest = ['l', 'l', 'o']
          

Example 2
first, *theRest, last  = 'Hello'
print('first =', first)
print('theRest =', theRest)
print('last =', last)

Output:
first = H
theRest = ['e', 'l', 'l']
last = o
            

Example 3
*EveryThing, = {0, 1, 2, 3, 4, 5}
print(EveryThing)

Output:
[0, 1, 2, 3, 4, 5]
          

Example 2
first, *theRest, last  = 'Hello'
print('first =', first)
print('theRest =', theRest)
print('last =', last)

Output:
first = H
theRest = ['e', 'l', 'l']
last = o
          

Example 4
*Numbers, = range(1, 6)
print(Numbers)

Output:
[1, 2, 3, 4, 5]
          

In Example 1 the first character of the string is assigned to first. The second character of the string is assigned to second. The remainder of the string is unpacked by the * operator and assigned to theRest.

In Example 2 The first character of the string is assigned to first. The last character of the string is assigned to last> The remaining characters of the string i.e. those between the first and last characters are assigned to theRest.

In Example 3 all of the elements of the set are unpacked by the * operator into the list; EveryThing. Note the comma that appears after *EveryThing. If this isn't there a syntax error will occur. The comma forces EveryThing to be of type list.

The * unpacking operator can also be used to pass a variable-length list of arguments to a function when the number of arguments is not known in advance. This is a very powerful feature and is discussed here.

Double Asterisk (**) Unpacking Operator

The double asterisk operator (**) is used to unpack dictionaries. The operator can be used to pass a variable-length list of key/value (keyworded) arguments to a function where the argument list is converted to a single dictionary - see Example 6.

The Python language allows the ** operator to combine dictionaries but sadly this functionality hasn't been ported to MicroPython (at least for the micro:bit) - see Example 5


Example 5 
# Demonstrate code that will combine
# dictionaries in the Python language.

# Unfortunately this doesn't work with
# MicroPython (for the micro:bit).

d1 = {'A':1, 'B':2}
d2 = {'C':3}
big_dict = {**d1, **d2}
print(big_dict)

Output:
Traceback (most recent call last):
  File "main.py", line 9
SyntaxError: invalid syntax
          

Example 6 
def convert_to_dict(**elements):
    return elements
    
Dict = convert_to_dict(A=1, B=2)  
print(Dict)

Output:
 {'A': 1, 'B': 2}
          

In Example 1 unfortunately the line big_dict = {**d1, **d2} causes a syntax error. This limits, some what, the usefulness of ** in the MicroPython environment.

In Example 2 the keyworded arguments are passed to the convert_to_dict() function where the double asterisk operator (**) converts them into a dictionary.