← back to section

As soon as you have more than one check, repetition shows up: the same calculation, the same three lines of setup. Copy them into ten places and you change ten places later. A function solves that: the code is written once and called by name.

What a function looks like

live example

def greet():
    print("Hello")

greet()      # the call: now it prints
Run

Running examples is part of paid access. There the same code runs inside the article: editor, run and check next to the paragraph. Three free days →

def means "I am defining a function", then the name, brackets and a colon. The body is indented. Until the function is called, nothing inside it happens.

Practice here: Your own function

Parameters: same work, different data

live example

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

greet("Anna")     # Hello, Anna
greet("Ivan")     # Hello, Ivan
Run

Running examples is part of paid access. There the same code runs inside the article: editor, run and check next to the paragraph. Three free days →

The name in brackets is a parameter: a variable that appears inside the function with the value passed at the call. Several parameters go through commas.

Practice here: A function with a parameter · Two parameters and a calculation

Returning a value

Printing from inside a function is almost always wrong: the result is needed in the code, not on the screen — to compare or pass on.

live example

# wrong way
def cost(price, quantity):
    print(price * quantity)

cost(4990, 2)
Run

Running examples is part of paid access. There the same code runs inside the article: editor, run and check next to the paragraph. Three free days →

live example

# right way
def cost(price, quantity):
    return price * quantity

total = cost(4990, 2)
print(total > 5000)
Run

Running examples is part of paid access. There the same code runs inside the article: editor, run and check next to the paragraph. Three free days →

The first can only show a number to a human. The second hands it to the code: return ends the function and passes the value out, and you compare it. Anything after return does not run.

Practice here: A function that answers yes or no

Default values

live example

def find_orders(orders, status="PAID"):
    return [o for o in orders if o["status"] == status]

orders = [
    {"id": "ord-042", "status": "PAID"},
    {"id": "ord-043", "status": "CANCELLED"},
]

print(find_orders(orders))                  # looks for PAID
print(find_orders(orders, "CANCELLED"))     # looks for cancelled
Run

Running examples is part of paid access. There the same code runs inside the article: editor, run and check next to the paragraph. Three free days →

A parameter with a default value can be left out: the usual case is built in, exceptions are passed explicitly.

One trap: a default value must not be a list or a dict.

live example

# wrong way
def add(item, into=[]):
    into.append(item)
    return into

print(add("a"))
print(add("b"))     # ['a', 'b'] — a trace of the previous call
Run

Running examples is part of paid access. There the same code runs inside the article: editor, run and check next to the paragraph. Three free days →

live example

# right way
def add(item, into=None):
    if into is None:
        into = []
    into.append(item)
    return into

print(add("a"))
print(add("b"))     # ['b'] — every call gets its own list
Run

Running examples is part of paid access. There the same code runs inside the article: editor, run and check next to the paragraph. Three free days →

The list in brackets is created once for the whole program, not on every call, and keeps values between them. With None it is created anew inside the function.

Practice here: A default value

Why a tester needs this

Test setup. The order a check runs on is built by a function: common parts go inside, differences become parameters.

live example

def order(status="PAID", total=4990):
    return {"id": "ord-042", "status": status, "total": total}

print(order())
print(order("CANCELLED", 890))
Run

Running examples is part of paid access. There the same code runs inside the article: editor, run and check next to the paragraph. Three free days →

One line per check instead of three, and one place to fix when the order changes.

Your own check. A repeating condition is extracted and named by meaning:

live example

def total_adds_up(order):
    computed = 0
    for item in order["items"]:
        computed += item["qty"] * item["price"]
    return computed == order["total"]

order = {"items": [{"qty": 1, "price": 4990}, {"qty": 2, "price": 890}], "total": 6770}
print(total_adds_up(order))     # True
Run

Running examples is part of paid access. There the same code runs inside the article: editor, run and check next to the paragraph. Three free days →

Such a function answers yes or no: the reader sees if not total_adds_up(order) and gets the intent without parsing arithmetic.

Practice here: Split a check into two parts

Naming and formatting

A function name is a verb or a question: find_orders, add, total_adds_up. If the name grows into do_everything_and_check, the function does too much and should be split.

Python has an agreed style guide, PEP 8. Four rules from it cover daily work: indent with four spaces (never mix tabs and spaces, Python complains); name functions and variables in lower case with underscores; put spaces around = in an assignment but not around a default value in the signature; keep a line under roughly a hundred characters.

live example

# wrong way: the name says nothing, spaces are random
def Check( order,minimum = 1000 ):
    return order [ "total" ]>=minimum

print(Check({"total": 4990}))
Run

Running examples is part of paid access. There the same code runs inside the article: editor, run and check next to the paragraph. Three free days →

live example

# right way: a question for a name, four spaces, even spacing
def total_at_least(order, minimum=1000):
    return order["total"] >= minimum

print(total_at_least({"total": 4990}))
Run

Running examples is part of paid access. There the same code runs inside the article: editor, run and check next to the paragraph. Three free days →

Both versions work the same, but the second reads at a glance — and code is read far more often than written. You do not police this by hand: the editor and the project formatter align the file on save.

In short

  • def name(params): defines a function; nothing happens until it is called.
  • return hands the result to the code and ends the function; printing from inside is almost always a mistake.
  • A default value makes a parameter optional; it must not be a list or a dict.
  • In checks, functions cover two jobs: test setup and your own check with a meaningful name.
  • The name is a verb or a question; an overly long name means the function does too much.