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.returnhands 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.
What to read next
- Lists and dicts — what the data a function takes and returns is made of.
- assert and errors — how a check fails readably.
- Working with data — count, compare, find the difference.
- pytest: your first real test — how these functions turn into a test you run with one command.