AnyLearn
All lessons
Programmingbeginner

Functions, Lists, and Dictionaries: Organising a Program

Split a program into named functions with parameters and return values, and hold data in lists and dictionaries. Learn why return is not print, what local scope protects, the accumulator pattern, how to choose list or dictionary, and the aliasing trap, then build a monthly budget checker.

Updated · AI-authored, review-gated · how lessons are made

Not signed in: your progress and quiz score won't be saved.
Progress1 / 10

Functions: naming a piece of work

Once a program passes a few dozen lines, the same calculations start to repeat. A function packages a piece of work under a name so you can run it whenever you need it:

def area(width, height):
    return width * height

print(area(3, 4))                    # 12
room = area(5, 2.5) + area(2, 2)
print(room)                          # 16.5

def starts the definition. width and height are parameters: names that receive whatever values the caller passes in. return sends a value back to wherever the function was called, so area(3, 4) becomes 12 in the expression that used it.

Defining a function does not run it. Python only records the instructions; the body runs each time you call the function with parentheses.

Key idea: A good function does one job you can describe in a sentence. If describing it needs "and", it probably wants to be two functions.

Full lesson text

All 10 steps on one page, for reading, reference, and search.

Show

1. Functions: naming a piece of work

Once a program passes a few dozen lines, the same calculations start to repeat. A function packages a piece of work under a name so you can run it whenever you need it:

def area(width, height):
    return width * height

print(area(3, 4))                    # 12
room = area(5, 2.5) + area(2, 2)
print(room)                          # 16.5

def starts the definition. width and height are parameters: names that receive whatever values the caller passes in. return sends a value back to wherever the function was called, so area(3, 4) becomes 12 in the expression that used it.

Defining a function does not run it. Python only records the instructions; the body runs each time you call the function with parentheses.

Key idea: A good function does one job you can describe in a sentence. If describing it needs "and", it probably wants to be two functions.

2. return is not print

The most common confusion with functions is treating print and return as the same thing. print shows a value on the screen. return hands a value back to the code that called the function. Only a returned value can be stored, compared or passed on.

Predict first

What does this program print?

def double(x):
    print(x * 2)

result = double(4)
print(result)

The fix is return x * 2 in the body. Then result is 8, and the caller decides whether to print it, add to it, or store it.

In practice: Let functions return values and keep print at the edges of the program. A function that returns can be reused and tested; one that only prints can only be looked at.

3. Parameters, defaults, and scope

Parameters can have default values, and callers can pass arguments by name:

def greet(name, greeting="Hello"):
    return f"{greeting}, {name}!"

print(greet("Sam"))                    # Hello, Sam!
print(greet("Sam", "Welcome"))         # Welcome, Sam!
print(greet(greeting="Hi", name="Ana"))  # Hi, Ana!

Variables created inside a function are local: they exist only while the function runs.

def add_tax(price):
    rate = 0.2
    return price * (1 + rate)

print(add_tax(50))   # 60.0
print(rate)          # NameError: name 'rate' is not defined

rate vanished when add_tax finished. This is a feature. Two functions can each use a variable called total without interfering, which is what lets large programs be built from small parts.

Key idea: Data goes into a function through its parameters and comes out through return. Keeping to that one route in and one route out makes a function predictable.

4. Lists: many values in order

A list holds several values in order, inside square brackets. Positions start at 0, exactly as with strings, but unlike strings a list can be changed after it is created.

temps = [18, 21, 25, 19]
temps.append(23)       # add to the end
temps[0] = 17          # replace an item
print(temps)           # [17, 21, 25, 19, 23]
OperationExampleResult with [17, 21, 25, 19, 23]
Lengthlen(temps)5
First, last itemtemps[0], temps[-1]17, 23
Slicetemps[1:3][21, 25]
Membership25 in tempsTrue
Sorted copysorted(temps)[17, 19, 21, 23, 25]
Built-in totalssum(temps), max(temps)105, 25

A list can hold any type, including strings, other lists, or a mix, though in practice most lists hold one kind of thing.

5. Looping over a list

A for loop over a list visits each item in order, which is how most processing is done. The pattern of keeping a running value while you loop is called an accumulator:

temps = [17, 21, 25, 19, 23]

total = 0
highest = temps[0]
for t in temps:
    total += t
    if t > highest:
        highest = t

print(total)               # 105
print(highest)             # 25
print(total / len(temps))  # 21.0

Python's built-ins sum(temps) and max(temps) give the same answers in one call, and you should use them in real code. It is still worth writing the loop once, because the same shape (start a value, update it for each item) solves problems no built-in covers: the longest word, the first negative number, the count of days above 20 degrees.

Gotcha: Starting highest at 0 instead of temps[0] looks harmless, and works until every temperature is negative. Then the "highest" is 0, a value that is not in the list.

6. Dictionaries: looking things up by name

A list finds items by position. A dictionary finds them by a key you choose, such as a name, a product or a word. Each key maps to a value:

stock = {"apples": 12, "pears": 0}
stock["plums"] = 30                   # add a new key
stock["apples"] = stock["apples"] - 2  # update a value

print(stock["apples"])          # 10
print("kiwis" in stock)         # False
print(stock.get("kiwis", 0))    # 0, a default instead of an error

for fruit, qty in stock.items():
    print(fruit, qty)           # apples 10 / pears 0 / plums 30

Keys are unique: assigning to an existing key replaces its value. Looking up a missing key with square brackets, stock["kiwis"], raises a KeyError; .get(key, default) returns the default instead. Since Python 3.7 the language guarantees that a dictionary keeps keys in the order they were inserted, which is why the loop prints apples, pears, plums.

7. List or dictionary?

Choosing the right container makes the rest of the code simple. The question to ask is: how will I find things later?

ListDictionary
Written as[18, 21, 25]{"apples": 12, "pears": 0}
Find an item byPosition: temps[2]Key: stock["apples"]
OrderThe order you built itInsertion order
DuplicatesAllowedKeys unique, values may repeat
Missing itemIndexErrorKeyError, or a default via .get()
Good forSequences: readings, steps, a queueLookups: prices by product, counts by word

The two nest naturally. A list of dictionaries is the standard shape for a table of records, one dictionary per row:

people = [
    {"name": "Ana", "age": 31},
    {"name": "Ben", "age": 27},
]
print(people[1]["name"])   # Ben

people[1] picks the second row by position, then ["name"] picks a field by key.

8. Gotcha: two names, one list

In the first lesson, b = a followed by a = 10 left b unchanged. With lists something that looks similar behaves differently:

a = [1, 2, 3]
b = a
b.append(4)
print(a)          # [1, 2, 3, 4]

b = a does not copy the list. It makes b a second name for the same list, so changing the list through b is visible through a. The earlier integer example was different because a = 10 pointed a at a new value; b.append(4) changes the existing list in place.

To get an independent copy, ask for one:

c = a.copy()
c.append(5)
print(a)          # [1, 2, 3, 4]
print(c)          # [1, 2, 3, 4, 5]

Gotcha: The same applies when you pass a list to a function. If the function appends to its parameter, the caller's list changes too. Return a new list instead unless changing the original is the whole point.

9. Worked program: a monthly budget checker

Everything in this lesson combines into a small but real program. Expenses are a list of dictionaries; limits are a dictionary from category to amount; two functions do the work.

expenses = [
    {"item": "rent", "category": "housing", "amount": 950},
    {"item": "groceries", "category": "food", "amount": 310},
    {"item": "bus pass", "category": "transport", "amount": 65},
    {"item": "takeaway", "category": "food", "amount": 120},
    {"item": "electricity", "category": "housing", "amount": 85},
]
limits = {"housing": 1000, "food": 400, "transport": 100}

def totals_by_category(items):
    totals = {}
    for e in items:
        cat = e["category"]
        totals[cat] = totals.get(cat, 0) + e["amount"]
    return totals

def over_limit(totals, limits):
    flagged = []
    for cat, spent in totals.items():
        if spent > limits.get(cat, 0):
            flagged.append(cat)
    return flagged

totals = totals_by_category(expenses)
print(totals)   # {'housing': 1035, 'food': 430, 'transport': 65}
print("Over limit:", over_limit(totals, limits))   # ['housing', 'food']

totals.get(cat, 0) + e["amount"] is the accumulator pattern with a dictionary: start each category at 0 the first time it appears, then keep adding.

10. How the budget program fits together

Data flows in through parameters and out through return values; only the last step prints.

flowchart LR
  A["expenses: list of dicts"] --> B["totals_by_category(items)"]
  B --> C["totals: dict of category to sum"]
  C --> D["over_limit(totals, limits)"]
  L["limits: dict"] --> D
  D --> E["flagged: list of categories"]
  C --> P["print results"]
  E --> P

Check your understanding

The lesson ends with a 5-question quiz. Take it in the player above to see your score.

  1. A function has no `return` statement. What does calling it give back?
    • 0
    • An empty string
    • The last value it printed
    • None
  2. `a = [1, 2]`, `b = a`, `b.append(3)`. What is `a` now?
    • [1, 2]
    • [1, 2, 3]
    • [3]
    • It raises an error
  3. Which is the best container for looking up a product's price by its name?
    • A dictionary with product names as keys
    • A list of prices
    • A string of all prices
    • A range of numbers
  4. `d = {"x": 1}`. What does `d.get("y", 5)` return?
    • KeyError
    • None
    • 5
    • 1
  5. Inside a function you write `count = 3`. After the function returns, what happens if the main program prints `count` (never defined outside)?
    • It prints 3
    • It prints 0
    • It prints None
    • NameError, because count was local to the function

Related lessons