The asterisk in Python

* multiplies and ** raises to a power, so 2 ** 10 is 1024. In a function definition, *args collects extra positional arguments into a tuple and **kwargs collects extra keyword arguments into a dict. In a function call or a list or dict literal, the same symbols do the reverse: they unpack.

Every * in Python

You write It means
a * b Multiply, or repeat a sequence: "ab" * 3 is "ababab"
a ** b a to the power of b
def f(*args) Collect extra positional arguments into a tuple
def f(**kwargs) Collect extra keyword arguments into a dict
def f(a, *, b) Everything after the bare * must be passed by keyword
f(*items) Unpack a list or tuple into separate arguments
f(**options) Unpack a dict into keyword arguments
[*a, *b] Build a new list from the items of a and b
{**a, **b} Merge two dicts; keys in b win
first, *rest = items Put the leftovers in a list
from module import * Import every public name
except* ValueError: Handle part of an exception group (Python 3.11+)

*args and **kwargs

In a function definition, a single * gathers any positional arguments that don't have their own parameter, and a double ** gathers any leftover keyword arguments:

def describe(name, *args, **kwargs):
    print(name, args, kwargs)

describe("asterisk", 1, 2, color="red", size=12)
# asterisk (1, 2) {'color': 'red', 'size': 12}

The names args and kwargs are only a convention. *parts and **options work the same, because the asterisks do the work. The order is fixed: normal parameters, then *args, then keyword-only parameters, then **kwargs.

Unpacking in calls

In a call, * spreads a sequence into positional arguments and ** spreads a dict into keyword arguments:

point = (3, 4)
options = {"sep": " -> ", "end": "!\n"}
print(*point, **options)
# 3 -> 4!

This is how wrapper functions pass everything through unchanged: return func(*args, **kwargs).

Unpacking in assignments and literals

first, *middle, last = [1, 2, 3, 4, 5]
print(first, middle, last)    # 1 [2, 3, 4] 5

combined = [*range(3), *"ab"]
print(combined)               # [0, 1, 2, 'a', 'b']

defaults = {"color": "black", "size": 12}
custom = {"size": 16}
print({**defaults, **custom}) # {'color': 'black', 'size': 16}

A starred name in an assignment always gets a list, even when it catches nothing: a, *b = [1] leaves b as [].

The bare *: keyword-only arguments

A * on its own in a parameter list means "no more positional arguments after this point". The built-in sorted uses it, which is why key must be named:

sorted(["bb", "a"], key=len)   # ['a', 'bb']
sorted(["bb", "a"], len)       # TypeError: sorted expected 1 argument, got 2

Its partner is /: parameters before a / can only be passed by position. def f(a, /, b, *, c) makes a positional-only, b either, and c keyword-only.

** as the power operator

Two details trip people up:

print(-2 ** 2)      # -4, because ** binds tighter than the minus sign
print((-2) ** 2)    # 4
print(2 ** 3 ** 2)  # 512, because ** groups right to left: 2 ** 9

For square roots, x ** 0.5 works, and math.sqrt(x) is clearer. pow(a, b, m) with a third argument computes powers modulo m far faster than a ** b % m for large numbers.

The sequence-repeat trap

* on a list repeats references, not copies. With nested lists, every row is the same object:

grid = [[]] * 3
grid[0].append("x")
print(grid)   # [['x'], ['x'], ['x']]

grid = [[] for _ in range(3)]
grid[0].append("x")
print(grid)   # [['x'], [], []]

from module import *

This imports every name in the module that doesn't start with an underscore, or exactly the names listed in its __all__. It's handy in the interactive shell and discouraged in real code by Python's style guide, PEP 8, because nobody reading the file can tell where a name came from.