CodingNic

Functions

Function Parameters

Functions 30 min read

Function Parameters

Objectives

By the end of this chapter, you should be able to:

  • Define what a parameter is, and why they’re essential
  • Use keyword arguments
  • Use default argument values
  • Write functions that accept an unknown number of arguments
  • Pass a function as an argument to another function

💡 Why this matters: A function that only ever does one fixed thing isn’t very useful. Parameters are what let the same function handle different input every time it’s called.

Functions with Arguments

python
def pet_names(cat_name, dog_name):
    return f"I have a cat named {cat_name} and a dog named {dog_name}."

This function takes two arguments and builds a message from them.

Keyword Arguments

Without keyword arguments, order matters. Get it wrong, and the values land in the wrong place:

python
pet_names("Mittens", "Fido")
# "I have a cat named Mittens and a dog named Fido."

pet_names("Fido", "Mittens")
# "I have a cat named Fido and a dog named Mittens." (swapped by accident)

Passing arguments by name, called keyword arguments, fixes that, and lets you pass them in any order:

python
pet_names(cat_name="Mittens", dog_name="Fido")
# "I have a cat named Mittens and a dog named Fido."

pet_names(dog_name="Fido", cat_name="Mittens")
# same result: order doesn't matter with keyword arguments

Default Argument Values

You can give a parameter a default value, used whenever the caller doesn’t provide one:

python
def add(a=5, b=15):
    return a + b

add(15, 1)  # 16
add(4)      # 19 (a is 4, b defaults to 15)
add()       # 20 (both default)
add(b=30)   # 35 (a defaults to 5, b is 30)

The syntax looks like keyword arguments, but the distinction matters: you use default values when defining a function, and keyword arguments when calling one.

The Mutable Default Argument Trap

Default values are only evaluated once, when the function is defined, not each time it’s called. That’s harmless for numbers and strings, but genuinely dangerous with a mutable default like a list or dictionary:

python
def add_item(item, items=[]):
    items.append(item)
    return items

add_item("apple")           # ['apple']
add_item("banana")          # ['apple', 'banana'], not ['banana']!

The same [] object is reused across every call that doesn’t pass its own items, so items pile up silently between unrelated calls. The fix is to default to None, and create a fresh list inside the function body instead:

python
def add_item(item, items=None):
    if items is None:
        items = []
    items.append(item)
    return items

add_item("apple")   # ['apple']
add_item("banana")  # ['banana'], a fresh list every time

This is one of the most common gotchas in Python. It’s worth remembering specifically because the buggy version looks completely reasonable until it bites you.

Accepting an Unknown Number of Arguments: *args

A single * before a parameter collects any number of positional arguments into a tuple:

python
def foo(*args):
    print(args)

foo(1, 2, 3)  # (1, 2, 3)
foo(1, 2)     # (1, 2)

That makes it easy to operate on all of them at once:

python
def add(*nums):
    return sum(nums)

add(1, 2, 3, 4)  # 10

Unpacking a List or Tuple into Arguments

The same * also works in reverse: when calling a function, it splits a list or tuple into separate arguments:

python
def add_three_nums(n1, n2, n3):
    return n1 + n2 + n3

add_three_nums(*[5, 6, 4])  # same as add_three_nums(5, 6, 4)

This is called unpacking. It’s essential when you have a collection of values but a function that expects them as separate arguments:

python
def add_and_multiply_numbers(a, b, c):
    return a + b * c

numbers = [1, 2, 3]
more_numbers = (4, 5, 6)

add_and_multiply_numbers(numbers)   # TypeError, a list isn't 3 separate arguments
add_and_multiply_numbers(*numbers)  # 7

add_and_multiply_numbers(more_numbers)   # TypeError
add_and_multiply_numbers(*more_numbers)  # 34

Accepting an Unknown Number of Keyword Arguments: **kwargs

** does the same thing for keyword arguments, collecting them into a dictionary:

python
def print_kwargs(a, b, **kwargs):
    print(a, b, kwargs)

print_kwargs(1, 2, awesome="sauce", test="yup")
# 1 2 {'awesome': 'sauce', 'test': 'yup'}

Unpacking a Dictionary into Keyword Arguments

And ** unpacks in reverse too, turning a dictionary into keyword arguments when calling a function:

python
def add_and_multiply_numbers(a, b, c):
    return a + b * c

data = dict(a=1, b=2, c=3)

add_and_multiply_numbers(data)     # TypeError
add_and_multiply_numbers(**data)   # 7

Since dictionaries are matched to parameters by key rather than position, this is especially useful when the data you have doesn’t come in a guaranteed order.

Passing a Function as an Argument

In Python, a function is just another value, like a number or a string. That means you can pass one function into another as an argument:

python
def is_even(num):
    return num % 2 == 0

def count_matches(numbers, check):
    total = 0
    for n in numbers:
        if check(n):
            total += 1
    return total

count_matches([1, 2, 3, 4, 5, 6], is_even)  # 3

count_matches doesn’t know or care what check actually does. It just calls check(n) on every number and counts how many times it gets back True. A function passed in this way is often called a callback.

Try It

  1. Write a function with two default-valued parameters, and call it three ways: no arguments, one argument, and using keyword arguments.
  2. Write a function that accepts *args and returns their sum.
  3. Build a dictionary matching a function’s parameter names, then call the function by unpacking it with **.
  4. Write a function is_positive(n) that returns True for positive numbers, then pass it as a callback into a function that counts how many positive numbers are in a list.

Recap

  • Keyword arguments let you pass values by name, in any order; default values are set when a function is defined, not when it’s called.
  • A mutable default value (like [] or {}) is created once and shared across every call that doesn’t override it. Default to None and create the mutable value inside the function instead.
  • *args collects extra positional arguments into a tuple inside a function; **kwargs does the same for keyword arguments, into a dictionary.
  • The same */** syntax also unpacks a list/tuple or dictionary into separate arguments when calling a function.
  • A function is a regular value, so it can be passed into another function as an argument (a callback).

Next lesson: function scope, what a function can and can’t see from outside itself.