Fundamentals · Lesson 17 of 94

Parameters, arguments, and return values

Watch this lesson

View full playlist ↗

Read the lesson

What you'll learn

Concept

A parameter is a name in the function's definition; an argument is the value you pass when you call it. In def line(name, price): the parameters are name and price; in line("coffee", 3.5) the arguments are "coffee" and 3.5. When the call happens, each argument is assigned to its parameter, and the body runs with those names bound.

Parameters, arguments, and return values: line('coffee', 3.5) → name = 'coffee' → price = 3.5 → body runs → return value goes back to the call
Parameters, arguments, and return values · Figure 1 · Open for full size

You can pass arguments two ways. Positional arguments are matched left to right. Keyword arguments name the parameter: line(price=3.5, name="coffee") works in any order and reads well when a function takes several values of the same type. Mixing is allowed as long as the positional ones come first. Use keywords whenever a call would otherwise be a row of unexplained numbers.

A parameter can have a default: def line(name, price, currency="$"). Callers who are happy with dollars pass two arguments; callers who are not pass three. Parameters with defaults must come after the ones without. Defaults are evaluated once, when the function is defined, which is fine for strings and numbers and a trap for lists; you will see that trap in the common mistakes at the end of this lesson.

Call name price currency
line("tea", 2) "tea" 2 "$" (default)
line("tea", 2, "€") "tea" 2 "€"
line(price=2, name="tea") "tea" 2 "$"
line("tea", currency="£", price=2) "tea" 2 "£"

return ends the function and sends a value back. To return several things, separate them with commas: return low, high sends a tuple, and the caller can unpack it: low, high = bounds(values). A function without return, or with a bare return, gives back None. Use None deliberately to mean "no result", such as a search that found nothing, and check for it with is None.

Variables created inside a function are local: they exist while the body runs and disappear when it returns. The caller cannot see them, and two calls do not share them. This is what makes functions safe to reuse: nothing leaks out except what you return. Reading an outer variable from inside a function works; assigning to one from inside creates a new local instead, which is the topic of the scope lesson later.

Parameters, arguments, and return values: main program; total = 0 → call add_tax(100) → inside: rate = 0.2; result = 120.0 → return result → main program; total = 120.0; rate does not exist here
Parameters, arguments, and return values · Figure 2 · Open for full size

Code

Positional, keyword, and mixed calls of the same function, plus a default in use. Every call produces the same shape of line.

# calls.py
def line(name, price, currency="$"):
    return f"{name:<10}{currency}{price:>6.2f}"

print(line("coffee", 3.5))
print(line("tea", 2, "€"))
print(line(price=4, name="juice"))
print(line("water", currency="£", price=1.25))
Output
coffee    $  3.50
tea       €  2.00
juice     $  4.00
water     £  1.25

Returning more than one value. The function returns a tuple; the caller unpacks it into two names in one line.

# bounds.py
def bounds(values):
    """Return the smallest and largest value as a pair."""
    return min(values), max(values)

low, high = bounds([12, 7, 31, 19])
print(low, high)
print(bounds([3, 3]))
Output
7 31
(3, 3)

Using None on purpose. A search that finds nothing returns None, and the caller checks for it with is None rather than guessing from a placeholder value.

# find_user.py
def find_user(users, name):
    for user in users:
        if user["name"] == name:
            return user
    return None

users = [{"name": "ada", "role": "admin"}, {"name": "linus", "role": "dev"}]
print(find_user(users, "ada"))
print(find_user(users, "grace"))
print(find_user(users, "grace") is None)
Output
{'name': 'ada', 'role': 'admin'}
None
True

Locals are private to the call. The rate created inside add_tax is gone after the return, and asking for it is a NameError.

# locals.py
def add_tax(amount):
    rate = 0.2
    return amount * (1 + rate)

total = add_tax(100)
print(total)
print(rate)
Output
120.0
Traceback (most recent call last):
  File "locals.py", line 7, in <module>
    print(rate)
          ^^^^
NameError: name 'rate' is not defined. Did you mean: 'range'?

Try it yourself

Create converter.py with a function convert(amount, rate=1.0, symbol="$") that returns a formatted string like "$12.50". Call it positionally, with keywords, and relying on both defaults. Then write split_bill(total, people) that returns two values: the share per person and the leftover cents when the total does not divide evenly, and unpack both from a call.

Common mistakes

What's next

You control what goes into a function and what comes out. Next you will handle the cases where you do not know how many arguments are coming, with *args and **kwargs.

Course overview