How to name, define, and call reusable blocks of code — and why organizing programs into functions makes them easier to read, test, and change
Programs grow quickly. A program that solves a real task can stretch from a handful of lines to hundreds in short order — and as it grows, keeping all of its logic in a single flat sequence becomes harder and harder to manage. Reading it requires following every detail at once. Testing it means running the whole thing. Changing one part risks breaking another. Functions are the tool that makes large programs manageable.
A function is a named block of instructions that can be called by name wherever its work is needed. Instead of repeating instructions, a program calls the function. Instead of reading every detail at once, a reader sees the function's name and understands its purpose at a high level. Functions make programs easier to write, easier to understand, easier to test, and easier to improve.
Chapter 9 introduces functions from the ground up: how to define them, how to call them, how to pass information in through parameters, how to receive results back through return values, and how to keep variables safely local to the functions that create them. The chapter then connects functions to the larger habits of decomposition and reuse — habits that experienced programmers apply every day regardless of the language or project they are working on.
A recipe contains twelve distinct steps: mixing flour, activating yeast, kneading dough, shaping loaves, and more. If someone asks you to explain the process, reading all twelve pages aloud is overwhelming. A better answer names the steps: "Mix the dough, let it rise, shape it, bake it." Programs face exactly the same challenge — and functions are the solution. By the end of this chapter, you will understand why naming pieces of a program is one of the most powerful ideas in all of computing.
Imagine that you are baking bread for the first time. You follow a recipe that runs twelve pages. Each page describes a different step: mixing flour, activating yeast, kneading dough, shaping loaves, adjusting the oven. The recipe works, but if someone asks you to explain the process, reading all twelve pages aloud is overwhelming. A better answer is to say: "I mix the dough, let it rise, shape it, and bake it." Each phrase names a step without describing every detail inside it.
Programs face the same problem. A program that solves a real task quickly grows beyond a few lines. If every instruction is written out in one flat sequence, the program becomes hard to understand, hard to test, and hard to change. When something goes wrong, finding the mistake in four hundred consecutive lines is exhausting. When the program needs to perform the same task twice, rewriting the same instructions twice introduces the chance of inconsistency.
Functions solve this problem. A function is a named block of instructions that can be called by name whenever its work is needed. Instead of repeating instructions, a program calls the function. Instead of reading every detail, a reader sees the function's name and understands its purpose.
Organize — Give a name to a group of related instructions.
Reuse — Call those instructions multiple times without rewriting them.
Test — Test one piece of the program independently of the others.
Maintain — Replace or improve one piece without rewriting everything else.
Read — Understand the program at a high level before diving into details.
Functions are not a new idea invented for programming. People use the same idea constantly. A recipe names steps. An instruction manual names procedures. A job description names responsibilities. In each case, a name stands for a collection of actions that can be referenced and repeated. In programming, functions make large programs possible — not because each line is visible at once, but because the program is organized into named pieces that each make sense on their own.
Before a function can be used, it must be defined. A function definition gives the function a name and specifies the instructions it will carry out.
In Python-style notation, a function definition looks like this:
Reading this definition carefully: the word def signals that a function definition is beginning. greet is the name of the function — chosen by the programmer to describe what it does. The parentheses () come after the name and will hold parameters when needed. The colon : ends the definition line. The indented lines underneath are the body of the function — the instructions that will run when it is called.
The function definition does not run the instructions. Defining a function is like writing a recipe: the recipe sits on the shelf until someone follows it. A function definition sits in the program until the function is called.
Not every computer science course uses Python. Throughout this book, pseudocode describes algorithms in plain, language-neutral terms. Here is the same function in pseudocode notation:
Choose function names that describe what the function does. A reader who sees greet() or calculate_area() immediately understands the purpose. A name like f() or do_thing() forces the reader to search for the body to understand anything.
Defining a function creates it. Calling a function runs it. In Python-style notation, a function call uses the function's name followed by parentheses:
The function is defined once and called three times. Each call runs the full body of the function. This is the first major benefit of functions: write once, call many times.
greet().greet function.In Python, a function must be defined before it is called. Calling greet() before writing def greet(): produces an error. The correct approach places all definitions above the calls that use them.
show_title() with three print statements in its body. If the function is called five times, how many total print statements execute?print_separator() that prints a line of twenty dashes. Then write three calls to it.The functions seen so far do the same thing every time they are called. That is useful, but limited. Most functions need to work with different data depending on the situation. A parameter is a variable listed in the function definition that represents information the function needs. When the function is called, the programmer supplies an argument — an actual value — for each parameter.
Each call passes a different argument. The parameter name takes the value of the argument for that call. The function's body uses name as a variable, so the output changes depending on what is passed in.
| Term | Where It Appears | What It Is | Example |
|---|---|---|---|
| Parameter | Function definition | A variable that receives a value when the function is called | def greet(name): — name is the parameter |
| Argument | Function call | The actual value passed to the function | greet("Amara") — "Amara" is the argument |
A function can have more than one parameter. Parameters are separated by commas in the definition. Arguments are matched to parameters by position — the first argument goes to the first parameter, the second argument to the second parameter, and so on.
def double(number):
result = number * 2
print(result)
double(5)
double(12)
double(100)
number = 5, result = 10. Second call: number = 12, result = 24. Third call: number = 100, result = 200.10
24
200Arguments are matched by position. If a function expects (name, age) and you call it with ("Jordan", 25), then name = "Jordan" and age = 25. Reversing them to (25, "Jordan") causes name = 25 and age = "Jordan" — almost certainly wrong.
def calculate_price(quantity, unit_price, discount):, identify each parameter.calculate_price with 5 items, a unit price of 12.99, and a 10% discount.def describe(animal, sound): print("A " + animal + " says " + sound + ".") — after the call describe("cat", "meow"), what values do animal and sound hold?multiply that takes two parameters and prints their product. Write three calls to it with different arguments.The functions seen so far have performed actions — printing output. But many functions need to return a result that the rest of the program can use. A return value is the result that a function sends back to whoever called it. In Python, the return statement specifies what value to send back.
The call double(7) runs the function with argument 7. The function computes 7 * 2 = 14 and returns 14. That returned value is stored in the variable result. Return values can also be used directly in expressions: print(double(5) + double(3)) adds two return values together before printing.
def celsius_to_fahrenheit(c):
return (c * 9 / 5) + 32
boiling = celsius_to_fahrenheit(100)
freezing = celsius_to_fahrenheit(0)
body_temp = celsius_to_fahrenheit(37)
212.0°F
32.0°F
98.6°F
1. It sends a value back to the caller.
2. It immediately ends the function's execution. Any instructions after a return statement inside a function will not run.
None — If a function ends without a return statement, Python automatically returns the special value None, which means "no value."
| Function Type | What It Does | When to Use It |
|---|---|---|
| Action function | Prints output, modifies a variable, or performs a task | When the program needs to do something, not compute something |
| Value function | Computes a result and returns it for the caller to use | When the result will be stored in a variable or used in an expression |
def mystery(n): return n * n - 1 — what value is stored in x after x = mystery(5)?def triple(n): print(n * 3)average that takes two numbers and returns their average. Then call it three times with different inputs.When a function runs, it can create its own variables. These are called local variables because they exist only inside the function. When the function ends, local variables disappear. The region of a program where a variable can be accessed is called its scope. A local variable is in scope inside its function and out of scope everywhere else.
Inside compute_area, the variable area is created and used. When the function returns, area no longer exists. Code outside the function cannot access area — it is local to the function. Parameters behave exactly like local variables: they exist inside the function for the duration of that call and disappear when the function returns.
Imagine each function call as a separate desk drawer that opens when the function starts and closes when it ends. Everything inside the drawer — local variables, parameters — is available while the drawer is open. When the drawer closes, everything inside disappears. The main program has its own workspace at a different level, separate from all function drawers.
If every function shared its variables with every other function, a change inside one function could accidentally affect another. Local variables are a protection mechanism: they keep functions self-contained. When reading a function, a programmer knows that local variables belong entirely to that function — there is no need to search the whole program to understand what they contain.
count in its body. Can code outside the function access count? Explain why or why not.def show_double(x): doubled = x * 2; print(doubled) — which variables are local to show_double?def calculate(): answer = 42 calculate() print(answer)Decomposition is the practice of breaking a complex problem into smaller, named parts that are easier to understand and solve separately. Functions are the primary tool for decomposition in programming. Instead of writing a large block of code to do everything at once, a programmer identifies the distinct tasks, writes a function for each one, and then assembles those functions into a whole.
This version is longer, but it is better organized. print_banner handles all banner printing — change it once and both banners change. get_number handles input with a prompt parameter. add handles the arithmetic. main describes the overall structure in plain, readable steps. The main function reads almost like a sentence: print a banner, get two numbers, add them, show the result, print another banner.
If you cannot summarize what a function does in one sentence, it may be doing too much. Consider breaking it into smaller functions. Good decomposition produces functions that each do one thing well, have a clear descriptive name, are small enough to read quickly, and can be tested independently.
One of the strongest arguments for functions is code reuse. When a task needs to be performed in more than one place, a function performs that task once and is called wherever it is needed. Repetition is not merely inconvenient — it is dangerous. Each copy of repeated logic is an opportunity for inconsistency. If the logic needs to change, every copy must be found and corrected. Missing one produces different behavior in different parts of the program.
temp1_f = (25 * 9 / 5) + 32
temp2_f = (0 * 9 / 5) + 32
temp3_f = (100 * 9 / 5) + 32
Formula appears three times. A typo in any copy produces a silent error.
def celsius_to_fahrenheit(c):
return (c * 9 / 5) + 32
temp1_f = celsius_to_fahrenheit(25)
temp2_f = celsius_to_fahrenheit(0)
temp3_f = celsius_to_fahrenheit(100)
Formula appears once. One change fixes all three.
The idea of code reuse extends far beyond individual programs. Programming languages come with libraries — collections of pre-written functions that programmers can call without writing them. Python's standard library includes functions for working with numbers, formatting dates, reading and writing files, generating random numbers, and sorting data. When a Python program calls len("hello"), it is reusing a function that someone else wrote and tested. The programmer reuses that function without seeing or managing its internal code. The skills developed in this chapter — clear functions, good names, parameters and return values — are the same skills used in large-scale reuse.
Writing a function that works is the first goal. Writing a function that is also clear, well-named, and easy to use is the professional standard. The habits in this section separate programs that merely run from programs that can be understood, maintained, and trusted.
A function's name is a promise to the reader about what it does. Compare f(x) — which tells nothing — with square(x), which tells everything. Good function names are usually verbs or verb phrases for functions that perform actions, and descriptive noun phrases or adjective phrases for functions that compute values.
| Pattern | Examples | Why It Works |
|---|---|---|
| Verb phrase for actions | print_report(), save_file() | Describes what the function does |
| Verb + noun for computations | calculate_average(scores), convert_to_celsius(f) | Names the result being produced |
is_ or has_ prefix for checks | is_valid_email(address), has_duplicates(list) | Signals a True/False return value |
A function should have a clear, single purpose. If a function does too many different things, it becomes hard to test, hard to reuse, and hard to understand. Consider separating a function that both computes a total and formats and prints a receipt into two functions: one that returns the total, one that prints the formatted output. Each function can then be tested, reused, and understood on its own.
A function that fits on one screen — roughly 20 to 30 lines — is usually easier to understand than a longer one. When a function grows much longer than that, it often signals that it contains more than one distinct task and could be decomposed further. This is a guideline, not an absolute rule. But brevity is a goal worth aiming for.
The comment explains the clamping behavior, which the name adjust_score does not fully convey. Brief, purposeful comments at the start of a function body are a professional habit — they explain what the function does, especially when the behavior has a constraint or nuance that the name alone cannot capture.
When a function is finished, test it by calling it with inputs whose correct outputs are already known. If the function returns the right results for known inputs, the programmer gains confidence that it works. If a result is wrong, the problem is isolated to that one function — not buried in a large program.
The concepts in this chapter come together in programs of meaningful size. Functions allow a programmer to manage complexity: to think at a high level when reading the structure, and at a detailed level when working on individual pieces. Two patterns make this possible in practice.
Many programs organize their top-level logic into a function called main. The main function describes what the program does step by step, using calls to other functions for each step. Reading main gives a full picture of the program's logic without showing any of the details.
Each function call names a step. A reader who wants to understand the details of one step looks at that function's definition. This layered structure — high-level description at the top, detailed implementation below — is exactly how large real-world programs are organized.
Here greet_all calls greet_one, which calls build_greeting. Each function does one clear job. The call stack — the record of which functions are currently running and where each will return — tracks this chain of calls. When build_greeting returns, execution goes back to greet_one. When greet_one returns, execution goes back to greet_all.
Functions are the foundation of every programming skill built in later courses. Data structures courses use functions to process collections of data. Web development courses use functions to handle user interactions. Software engineering courses use functions as the basic unit of testing, documentation, and collaboration. The habits developed here — naming well, one purpose per function, using parameters and return values, decomposing problems — are habits that experienced programmers apply every single day.
main function for a program that: greets the user, asks for their name, and prints a personalized message. Define the three helper functions it would call.collect_data, process_data, and display_results. Describe the call stack as the program runs from start to finish.def, a name, parentheses, a colon, and an indented body. Defining a function does not run it — it only creates it, waiting to be called.return statement sends a value back to the caller and immediately ends the function. Action functions print or modify; value functions compute and return. A function without return sends back None.main function reads like a step-by-step description of the whole task.For each program, trace the execution step by step and write what the program prints. Work through the code by hand — do not run it.
→ Trace the output.
→ Trace the output. Pay close attention to the last line.
→ Trace the output.
→ Trace the output. Notice that sum_of_squares calls square.
→ Trace the output.
Fix: Move the function definition above the call, or restructure so all definitions come first.
Fix: Supply the correct number of arguments to match the number of parameters.
Fix: If the program needs to use the result in an expression, add a return statement.
Fix: Use a return statement to send the value out of the function, and store it in a variable in the calling code.
Fix: Arguments must be supplied in the same order the parameters are defined. name comes first, then age.
Each program contains one or more errors. Identify each error, explain what is wrong, and write a corrected version.
→ What is wrong? Write the corrected version.
→ The program prints None instead of 10. What is missing? Correct it.
→ The program never prints "Multiplied!" Why? Is this a bug or expected behavior? Explain.
→ What is wrong on the line result = count_up? How should it be written?
→ The output is garbled. What is wrong? Write the corrected function call.
def add(a, b): return a + b
x = add(3, 4)
print(x)
print(add(x, 10))def area(base, height): return 0.5 * base * heightgreet("Jordan", "evening"). The definition is def greet(time, name):. What values do each parameter receive?square that takes one number and returns its square.is_passing that takes a score and returns True if the score is 60 or above, False otherwise.repeat_message that takes a message and a number, and prints the message that many times.def show(n): print(n) and then calls x = show(5) + 1. The program crashes. What is wrong and how should it be fixed?print statement after the return in a function. What happens to that statement? Why?For each scenario, identify which functions you would write and how they would work together.
main down to the helpers.These questions draw on material from earlier chapters.
def find_larger(a, b): if a > b: return a else: return bcollect_data, process_data, and display_results. The main function calls them in order. Describe what the call stack looks like at each stage of execution.average that takes three numbers as parameters and returns their average. Test it with at least three different sets of inputs by predicting the output before running.celsius_to_kelvin that converts Celsius to Kelvin (Kelvin = Celsius + 273.15). Return the result rather than printing it. Then write a caller that uses the return value.return statement do? What happens if a function has no return statement?Chapter 9 established functions as the primary tool for organizing and reusing code. Chapter 10 turns to a challenge that every programmer faces from the very first program: mistakes. No program works perfectly on the first attempt. Errors appear in syntax, in logic, and at runtime. Chapter 10 introduces the three major categories of programming errors, systematic strategies for finding and fixing them, the discipline of testing programs with specific known inputs, and principles for designing programs that are less likely to contain mistakes. Every skill introduced in Part II — variables, control flow, functions — will be revisited through the lens of debugging and testing. These habits apply to every program a student will write, in this course and beyond.