python · level 3

Functions

def, *args / **kwargs, defaults, lambdas.

100 XP

Python Functions

def declares a function. Indentation is the body.

def greet(name):
    return f"Hello, {name}"

Functions are first-class values: pass them around, return them, store them in lists.

Default + keyword arguments

def fetch(url, timeout=30, retries=3):
    ...

fetch("https://x")
fetch("https://x", timeout=60)
fetch("https://x", retries=5, timeout=60)   # any order via keyword

*args and **kwargs

*args collects positional, **kwargs collects keyword:

def log(*args, **kwargs):
    print(args)        # tuple of positional values
    print(kwargs)      # dict of keyword pairs

log(1, 2, level="info")
# (1, 2)
# {'level': 'info'}

The * and ** also work on the call site to unpack:

nums = [1, 2, 3]
print(*nums)              # like print(1, 2, 3)

opts = {"timeout": 30, "retries": 3}
fetch("https://x", **opts)

Positional-only / keyword-only

A bare * in the parameter list forces keyword arguments after it:

def reset(user_id, *, hard=False):
    ...

reset(1, hard=True)       # ok
reset(1, True)            # TypeError — must use keyword

A bare / forces positional before it (3.8+):

def divide(a, b, /):
    return a / b

divide(6, 2)              # ok
divide(a=6, b=2)          # TypeError

Lambda

A one-expression anonymous function. No statements allowed inside.

square = lambda x: x * x

sorted(users, key=lambda u: u.age)

If you need more than one line, write a regular def — even if you only use it once. Readability beats cleverness.

Default-argument footgun

Default values are evaluated once at definition time, not on every call. Mutable defaults are shared:

def append(item, target=[]):       # BUG
    target.append(item)
    return target

append(1)                          # [1]
append(2)                          # [1, 2] — surprise!

Fix:

def append(item, target=None):
    if target is None:
        target = []
    target.append(item)
    return target

Docstrings

The first string in a function is its docstring. Tools (help, IDE hovers, Sphinx) pick it up:

def slugify(s):
    """Convert a string to a URL-friendly slug.

    Lowercases, replaces non-alphanumerics with hyphens, strips
    leading/trailing hyphens.
    """
    ...

Type hints

def fetch(url: str, timeout: int = 30) -> dict[str, str]:
    ...

Optional and ignored at runtime. Pair with mypy or pyright for actual checking.