Packing and Unpacking

Indices and tables

Definition

The use of the * and ** operators can be used to pack values into, and unpack values from iterables.

The ways it can be used differ depending on whether you are dealing with assignments, or functions.

When used as part of assignment, they are called a starred expression.

When used as part of a function, they are called starred arguments.

Starred Expressions

Starred expressions come about because of the (simplified) definition of assignment shown below:

target ("," target)* = expression ("," expression)*
  • expression is something that yields a single value or an iterable. Multiple expressions can be given that are separated by a “,” (and due to the “,” it yields a tuple regardless of the presence of the () brackets).

  • One or more target variables to receive values generated by (unpacked from) their corresponding expression. A target can be a name, attribute or index/slice of a mutable data type. A target with a * in front, as in *target, makes it a starred expression

    • The *target gets (is packed with) the leftover values (if any) not assigned to the other targets.

Generally speaking, multiple groups of targets on the left, can receive values from unpacking multiple iterables (generated by their corresponding expression) in the expression list on the right.

That’s a mouthful … and certainly more powerful functionality then you need on a daily basis.

Some examples will make this clearer!

  • From a literal tuple (the , on the right makes it a tuple):
../../_images/starred_expressions0.png

Fig. 36 Starred Expressions Example 0

  • From a literal tuple into a starred expression (leftover items present go in c):
../../_images/starred_expressions1.png

Fig. 37 Starred Expressions Example 1

  • From a literal tuple into a starred expression (leftover items present go in c):
../../_images/starred_expressions2.png

Fig. 38 Starred Expressions Example 2

  • From a literal tuple into a starred expression (no leftover items present, c is empty):
../../_images/starred_expressions3.png

Fig. 39 Starred Expressions Example 3

  • From a named tuple into a starred expression (leftover items present go in c):
../../_images/starred_expressions4.png

Fig. 40 Starred Expressions Example 4

  • From a named tuple into a starred expression (no leftover items present, c is empty):
../../_images/starred_expressions5.png

Fig. 41 Starred Expressions Example 5

  • Target-list of target-lists to assign to from a tuple of tuples.

    • Combines multiple assignment operations into one statement.
    • Crazy … probably too crazy to maintain. Perhaps better not to do this.
../../_images/starred_expressions6.png

Fig. 42 Starred Expressions Example 6

In addition to what is shown above:

  • Anywhere a tuple was used on the right hand side, any other iterable, like a string or list, could also be used.

  • Anywhere a tuple was used on the left, you can also use:

    • An item of a mutable iterable
    • A slice of a mutable iterable
    • An attribute reference

    For example:

    >>> a = ['a', 'b', 'c', 'd']
    >>> a[3] = 'x'
    >>> a
    ['a', 'b', 'c', 'x']
    >>> a[1:3] = 'gh'
    >>> a
    ['a', 'g', 'h', 'x']
    

Tip

You can get pretty complicated with assignments in Python if you want to terrorize those that read your code. Don’t do it. Don’t be that guy. Not only will you make your code difficult to maintain by others, it will also be difficult for you to figure out what you were doing in 6 months when you come back to it.

Starred Arguments

A starred argument refers to the use of the * and ** operators to unpack a sequence or mapping in order to supply values to formal positional or keyword arguments in a function call.

Note

There is a related concept, which is Python’s support for a Variable Parameter List using the *args and **kwargs as the catch-all for extra positional arguments and extra keyword arguments, respectively. To be clear, that concept refers to how the supplied parameter values are collected and presented to the code inside the function when writing the function definition.

The * operator will unpack a sequence as positional arguments. For example:

>>> my_args = [1, 2, 3]
>>> def my_fn(a, b, c):
...     print("a: {}  b: {}  c: {}".format(a, b, c))
>>> my_fn(*my_args)
a: 1  b: 2  c: 3
../../_images/starred_arguments0.png

Fig. 43 Starred Arguments Example 0

If there are more positional parameters supplied than required, you will get a TypeError. For example:

>>> my_args = [1, 2, 3, 4]
>>> def my_fn(a, b, c):
...     print("a: {}  b: {}  c: {}".format(a, b, c))
>>> my_fn(*my_args)
TypeError: my_fn() takes 3 positional arguments but 4 were given
../../_images/starred_arguments1.png

Fig. 44 Starred Arguments Example 1

However, if the parameter *args is defined, it will pick up any extra positional parameters supplied. For example:

>>> my_args = [1, 2, 3, 4, 5, 6]
>>> def my_fn(a, b, c, *args):
...     print("a: {}  b: {}  c: {}  args: {}".format(a, b, c, args))
>>> my_fn(*my_args)
a: 1  b: 2  c: 3  args: (4, 5, 6)
../../_images/starred_arguments2.png

Fig. 45 Starred Arguments Example 2

The ** operator will unpack a mapping as keyword arguments. For example:

>>> my_kwargs = {'t': 10, 's': 9, 'r': 8}
>>> def my_fn(r, s, t):
...     print("r: {}  s: {}  t: {}".format(r, s, t))
>>> my_fn(**my_kwargs)
r: 8  s: 9  t: 10
../../_images/starred_arguments3.png

Fig. 46 Starred Arguments Example 3

If there are more keyword parameters supplied than required, you will get a TypeError. For example:

>>> my_kwargs = {'t': 10, 's': 9, 'r': 8, 'u': 99}
>>> def my_fn(r, s, t):
...     print("r: {}  s: {}  t: {}".format(r, s, t))
>>> my_fn(**my_kwargs)
TypeError: my_fn() got an unexpected keyword argument 'u'
../../_images/starred_arguments4.png

Fig. 47 Starred Arguments Example 4

However, if the parameter **kwargs is defined, it will pick up any extra keyword parameters supplied. For example:

>>> my_kwargs = {'t': 10, 's': 9, 'r': 8, 'u': 99}
>>> def my_fn(r, s, t, **kwargs):
...     print("r: {}  s: {}  t: {}, kwargs: {}".format(r, s, t, kwargs))
>>> my_fn(**my_kwargs)
r: 8  s: 9  t: 10, kwargs: {'u': 99}
../../_images/starred_arguments5.png

Fig. 48 Starred Arguments Example 5

Python allows you to unpack both sequences and mappings in the same function call as long as the functions formal arguments support it.

Try it!

Try the following:

  • Write a function definition that takes in the following:

    • 2 positional arguments called, “pos0” and “pos1”.
    • 2 keyword arguments called,”kw0” and “kw1”.
    • 0 or more additional positional arguments.
    • 0 or more additional keyword arguments.

    Use pass for the body of the function.

  • Call the function you defined above with the following values:

    • pos0 = 5
    • pos1 = 2
    • pos2 = (‘x’, ‘y’)
    • pos3 = ‘abc’
    • kw0 = ‘yes’
    • kw1 = ‘no’
    • kw2 = ‘up’
    • kw3 = ‘down’

    If the function call works, you won’t get any output (due to the pass statement), but you also won’t get any errors.