← Home

069. '*Args (Variable Positional Arguments)'

Accept arbitrary numbers of positional arguments

069. ‘*Args (Variable Positional Arguments)’

Aryan’s monitor has a helper that averages memory readings. Sometimes there are 5 readings in the window, sometimes 60 — the count varies. With fixed parameters he’d need a different function for each count. With *args he writes it once:

def average_pct(*readings):
    """Average any number of memory percentage readings."""
    if not readings:
        return 0.0
    return sum(readings) / len(readings)

# Works for any call count
avg5  = average_pct(72.1, 73.4, 71.8, 74.0, 72.9)
avg1  = average_pct(85.0)
avg0  = average_pct()          # returns 0.0

# Pass a pre-existing list with unpacking
window = [72.1, 73.4, 71.8]
avg    = average_pct(*window)  # unpack list → positional args

He also uses *args for a flexible alert function that can flag multiple processes at once:

def flag_processes(*names):
    return [name for name in names if name in WATCHED_SET]

flag_processes("Chrome", "Slack", "malware.exe")

Inside the function, *args (or *readings, *names) is always a tuple — you can iterate it, call len(), sum(), min(), max().


💡 Fun fact: Variadic functions (accepting a variable number of arguments) exist in almost every language. In C, printf(format, ...) is variadic — but C has no way to know how many arguments were passed, so it relies on the format string to count them. Python’s *args is far safer: len(args) always gives the exact count.

⚠️ Watch out: Inside the function, *args is a tuple, not a list — you cannot append to it or modify it. Beginners often try args.append(x) and get an AttributeError. If you need to modify the collected arguments, convert first: items = list(args).

🤔 Think about it: add_all(*window) unpacks a list into positional arguments, and def add_all(*numbers) repacks them into a tuple. Why does Python do this round-trip instead of just passing the list directly? What does this design decision enable?

Learning objectives

  • Use *args to accept variable numbers of positional arguments
  • Access *args as a tuple inside the function
  • Call functions that use *args with any number of arguments

Key concepts

  • *args
  • variadic functions
  • tuple

Try it

Concept detail

*args in a function definition collects extra positional arguments into a tuple.

def f(*args): — args is always a tuple (possibly empty) def f(a, b, *rest): — a and b are required; rest gets any extras as a tuple

Inside the function: len(args) — how many were passed sum(args) — works because args is a tuple of numbers for x in args: — iterate normally

Calling with a pre-built list (unpacking): items = [1, 2, 3] f(*items) — same as f(1, 2, 3)

Combining with regular parameters: def log(level, *messages): for msg in messages: print(f“[{level}] {msg}“) log(“INFO”, “Starting”, “Ready”, “Running”)

*args vs passing a list: add_all(1, 2, 3) — *args style: clean at call site add_all([1, 2, 3]) — list style: requires the caller to build a list first

Use *args when the function naturally handles “zero or more” of the same thing.

Solution

def add_all(*numbers):
    return sum(numbers)

def multiply_all(*numbers):
    result = 1
    for n in numbers:
        result *= n
    return result

def stats(*numbers):
    return {
        "min": min(numbers),
        "max": max(numbers),
        "mean": sum(numbers) / len(numbers),
        "count": len(numbers),
    }

Tests

def test_add_two():
    assert add_all(1, 2) == 3

def test_add_many():
    assert add_all(1, 2, 3, 4, 5) == 15

def test_add_one():
    assert add_all(42) == 42

def test_multiply_two():
    assert multiply_all(6, 7) == 42

def test_multiply_all():
    assert multiply_all(2, 3, 4) == 24

def test_multiply_one():
    assert multiply_all(5) == 5

def test_stats():
    result = stats(10, 20, 30, 40)
    assert result["min"] == 10
    assert result["max"] == 40
    assert result["mean"] == 25.0
    assert result["count"] == 4

def test_stats_two():
    result = stats(3, 7)
    assert result["min"] == 3
    assert result["max"] == 7
    assert result["count"] == 2

Resources