Skip to content
Study Hall
Lesson 5 of 5Computer Programming
Unit 5 · Lesson 5 of 5

Functions, Errors and Debugging

Packaging code so it can be reused and tested, and a method for finding a bug instead of guessing at one.

5 min read9 practice questionsAsk AI about this →

Key things to remember

  1. 1A parameter is the name in the definition; an argument is the value passed in the call.
  2. 2return sends a value back to the caller; print only displays it.
  3. 3A function with no return returns None.
  4. 4Local variables exist only inside their function and cannot clash with names outside.
  5. 5Syntax errors stop the program before it starts; runtime errors stop it mid-run; logic errors do not stop it at all.
  6. 6Read the LAST two lines of a traceback first.
  7. 7Test with normal, boundary and invalid inputs.

A function is a named block of code that does one job. You write it once and call it as many times as you like. The benefit is not really the saved typing — it is that a bug in a function is fixed in one place, and that a function with a clear input and output can be tested on its own, away from the rest of the program.

PythonWhat it does
def area(width, height):Define a function with two parameters
return width * heightCompute and send the result back
a = area(5, 3)Call it with arguments 5 and 3; a becomes 15
def greet(name="student"):A default value, used when no argument is given
def show(msg): print(msg)No return — this one acts, rather than producing a value
Parameters and arguments
A parameter is the name in the definition — the empty slot. An argument is the actual value you pass when calling. def area(width, height) declares two parameters; area(5, 3) supplies two arguments. Order matters: the first argument fills the first parameter.
print is not return
print() displays something on screen for a human. return hands a value back to the code that called the function, so it can be stored, added or passed on. A function that prints instead of returning cannot be used in a calculation: total = show_area(5, 3) sets total to None, because a function with no return returns nothing.

Scope

A variable created inside a function is local: it exists only while that function runs, and the rest of the program cannot see it. This is a feature, not a limitation. It means you can use i or total inside a function without any risk of clashing with a variable of the same name elsewhere. Anything the function needs from outside should arrive as a parameter, and anything it produces should leave via return.

ALocal variable
  • Created inside a function
  • Exists only while that function runs
  • Invisible to the rest of the program
  • Safe — cannot clash with anything outside
BGlobal variable
  • Created at the top level of the file
  • Readable from anywhere
  • Needs the global keyword to be modified inside a function
  • Avoid: any function might change it, which makes bugs hard to locate

The three kinds of error

TypeWhen it appearsExampleHow you find it
SyntaxBefore the program runsMissing colon, unbalanced bracketPython refuses to start and names the line
RuntimeWhile running, at the moment it happensDivision by zero, IndexError, TypeErrorThe program crashes with a traceback
LogicNever — the program runs fineUsing + where * was meantOnly by testing against known answers
The later an error shows itself, the more it costs you.you type itit runsit shipsSYNTAXBefore the program runs at allprint("hi" ← missing bracketPython refuses to startRUNTIMEPart-way through, at the failing linemarks[6] on a 6-item listCrashes with a tracebackLOGICNever — it runs and looks finetotal = total * m, meant +Wrong answer, no warningOnly testing against answers you worked out yourself will find a logic error — the computer will never tell you.
Fig 1When each kind of error announces itself — and what it costs you.
Logic errors are the dangerous ones
A syntax error stops the program before it starts. A runtime error stops it at the point of failure and tells you where. A logic error does neither — the program runs happily and produces a wrong answer, confidently. The only defence is testing against results you have worked out independently.

Reading a traceback

A Python traceback reads bottom-up in importance. The last line names the error type and gives a message: ZeroDivisionError: division by zero. Just above it is the line of your code that caused it, with its line number. Everything higher is the chain of calls that led there. Most students skim the whole block; reading only the last two lines usually identifies the problem immediately.

Error messageWhat it usually means
NameError: name 'x' is not definedTypo in a variable name, or used before assignment
TypeError: can only concatenate strMixing a string and a number — usually a missing int()
IndexError: list index out of rangeAn index past the end — usually len() instead of len()-1
ZeroDivisionErrorDividing by a variable that turned out to be 0 — often an empty list
IndentationErrorInconsistent indentation, often mixed tabs and spaces
ValueError: invalid literal for int()int() was given text that is not a number

A method for debugging

  1. Reproduce it. Find the exact input that triggers the bug every time. A bug you cannot reproduce cannot be confirmed fixed.
  2. Read the error message properly. The last two lines of a traceback name the error type and the exact line.
  3. Narrow it down. Add a print() showing the value of key variables just before the failing line, or comment out sections until the bug disappears. The last thing you removed contains it.
  4. Trace by hand. Build a trace table with one row per loop pass. Where your table and the program disagree is where the bug lives.
  5. Fix one thing. Change a single thing and re-test. Changing three at once means not knowing which one mattered.
  6. Re-test everything. Confirm the fix works and that the inputs that used to work still do.
The print() that finds anything
print(f"DEBUG i={i} total={total}") inside a loop shows you exactly what the program believes, pass by pass. It is unglamorous and it works. The moment the printed values stop matching what you expected is the moment the bug happened — and that is usually one line earlier than where the crash occurred.

Testing means choosing inputs deliberately rather than trying whatever comes to mind. Use a normal value that should obviously work, a boundary value right at a limit, and an invalid value that should be rejected. For a program that grades marks out of 100, that means testing 75, then 0, 50 and 100, then −5 and 150 and the word "hello". Programs that only ever see reasonable input are the ones that fail in front of a class.

Worked examples

Reveal one step at a time and try to beat it to the next line.

Example 1
Write a function that converts Celsius to Fahrenheit, then use it to print a table from 0 to 100 in steps of 20.
Example 2
This function is meant to return an average but always returns None: def average(nums): total = sum(nums) print(total / len(nums)). Diagnose and fix it.
Example 3
A program that sums a list gives a total far too small. Describe how you would locate the bug systematically.

Where marks get lost

  • Using print inside a function where return is needed, so the caller receives None.
  • Expecting a variable created inside a function to be visible outside it.
  • Calling a function before defining it — in Python the def must be executed first.
  • Passing arguments in the wrong order, so area(3, 5) and area(5, 3) silently mean different things.
  • Reading only the first line of a traceback. The last two lines carry the useful information.
  • Changing several things at once while debugging, then not knowing which change fixed it.
  • Testing only with input that works, so boundary and invalid cases fail in front of an audience.

Flashcards 10

Parameter vs. argument
return vs. print
What does a function with no return give back?
What is a local variable?
+ 6 more

Practice

1
A function ends with print(result) instead of return result. What does x = my_function() assign to x?
2
Which error type allows the program to run to completion while producing wrong answers?
3
What keyword sends a value back from a function to the code that called it?
4
A variable count is created inside a function. What happens if the main program tries to print it?
5
Match each error message to its usual cause.
NameError
TypeError
IndexError
ZeroDivisionError
6
For def power(base, exponent):, what does power(2, 3) compute?
7
Order these debugging steps.
  1. 1Read the error message and identify the failing line
  2. 2Change one thing and re-test
  3. 3Add print statements or trace by hand to narrow it down
  4. 4Reproduce the bug with a specific input
8
Which set of test inputs is best for a program that accepts marks from 0 to 100?
9
Why is it better for a function to return a value than to print it?

Spot a mistake on this page? Let us know