A function is a named, reusable block of code that does one job. Instead of writing the same logic in five different places, you write it once inside a function and call it whenever you need it. Functions make programs shorter, easier to read, easier to fix, and easier to explain — which is why every professional programmer uses them constantly. This article covers how to define and call functions, how to pass information in using parameters, how to send results back using return, and one of the most important concepts in programming: variable scope.
1 Why Do We Need Functions?
Suppose you are writing a quiz program. At the end of each round, you need to calculate a percentage, print a grade, and print a congratulations message. You have three rounds — so that logic appears three times in your program.
Without functions, you copy and paste those lines. Then you notice a bug — a typo in the congratulations message. You fix it in one place. You forget to fix the other two. Now your program has three versions of the same logic, and one of them is wrong.
Every room in a house needs electricity, but that does not mean every room has its own power station. The power station is built once and connected everywhere. A function is your program's power station: built once, used everywhere. Fix it in one place and every room benefits automatically.
Functions also give you something harder to measure but equally valuable: the ability to give a chunk of logic a name. A well-named function makes code read almost like English:
# Without functions — what does this code do? You have to read every line.
if score / total * 100 >= 60:
print("Pass")
# With functions — the intent is obvious immediately.
if calculate_percentage(score, total) >= 60:
print("Pass")The function version does not just work — it explains itself.
2 Defining and Calling a Function
There are two distinct steps with every function: defining it (writing it once) and calling it (running it whenever you need it).
2.1 The Anatomy of a Function Definition
def greet():
print("Hello, welcome to the program!")
print("Let's get started.")| Part | What it is |
|---|---|
def | The keyword that tells Python you are defining a function. |
greet | The function's name — you choose this. Same rules as variable names: snake_case, descriptive. |
() | Parentheses — required. This is where parameters go (see Section 3). Empty parentheses mean no parameters. |
: | A colon, just like if and while. Signals the start of the function body. |
| Indented body | The code that runs when the function is called. Must be indented exactly one level. |
2.2 Calling a Function
Defining a function does not run it. It just registers the name and the code to run later. To actually execute the function, you call it by writing its name followed by parentheses:
def greet():
print("Hello, welcome to the program!")
print("Let's get started.")
# Nothing has printed yet — the function is defined but not called.
greet() # Now it runs.
greet() # Call it again — runs a second time.
greet() # And again.Output:
Hello, welcome to the program! Let's get started. Hello, welcome to the program! Let's get started. Hello, welcome to the program! Let's get started.
The same function body runs every time greet() is called. This is the payoff of writing it once.
Python reads your file from top to bottom. If you call a function before defining it, Python will not know what it is yet and will raise a NameError. Always write your function definitions near the top of your file, before the code that calls them.
- What is the difference between defining a function and calling a function?
- How many times does the body of a function run when you define it? How many times does it run when you call it three times?
This code has an error. What is it?
say_hello() def say_hello(): print("Hello!")- Write a function called
print_dividerthat prints a line of 30 dashes (-). Then call it three times.
3 Parameters and Arguments
The greet() function above always does the same thing. That is useful, but limited. What if you want to greet a specific person by name? You need a way to pass information into a function. That is what parameters are for.
A toaster is a function: it does one job (toasting). But you put something in — bread, a bagel, a waffle — and the result depends on what you gave it. The slot is the parameter (a place for input to go); the bread you actually put in is the argument (the specific input for this particular use). Same toaster, different results depending on what you feed it.
3.1 Parameters vs. Arguments — The Exact Distinction
These two words are often used interchangeably in casual conversation, but they mean different things and it is worth being precise:
| Term | Where it appears | What it is | Example |
|---|---|---|---|
| Parameter | In the def line, inside the parentheses. | A placeholder variable name — a slot waiting to be filled. | def greet(name): — name is the parameter. |
| Argument | In the function call, inside the parentheses. | The actual value passed in to fill the parameter slot. | greet("Alex") — "Alex" is the argument. |
Think of it this way: the parameter is the label on the toaster slot; the argument is the actual bread you push in.
3.2 A Function With One Parameter
def greet(name):
print("Hello,", name + "!")
print("Welcome to the program.")
greet("Alex") # "Alex" is the argument
greet("Priya") # "Priya" is the argument
greet("Sam") # "Sam" is the argumentOutput:
Hello, Alex! Welcome to the program. Hello, Priya! Welcome to the program. Hello, Sam! Welcome to the program.
Each call passes a different argument. The parameter name takes on that value inside the function body. Same function, three different results.
3.3 Multiple Parameters
A function can take more than one parameter. Separate them with commas — both in the definition and in the call. Order matters: the first argument goes to the first parameter, the second to the second, and so on.
def describe_student(name, score):
print(name, "scored", score, "points.")
if score >= 60:
print(name, "has passed.")
else:
print(name, "has not passed.")
describe_student("Alex", 85)
describe_student("Sam", 42)Output:
Alex scored 85 points. Alex has passed. Sam scored 42 points. Sam has not passed.
3.4 Default Parameter Values
You can give a parameter a default value — a fallback that is used when the caller does not provide that argument. Default parameters are placed at the end of the parameter list.
def greet(name, greeting="Hello"):
print(greeting + ",", name + "!")
greet("Alex") # uses default greeting
greet("Priya", "Good morning") # overrides the defaultOutput:
Hello, Alex! Good morning, Priya!
If you define a function with two parameters and call it with only one argument (and no default is set), Python raises a TypeError: "missing 1 required positional argument". If you pass too many arguments, you get a different TypeError. The number of arguments in the call must match the number of parameters in the definition — unless a default value covers the gap.
- In your own words, what is the difference between a parameter and an argument?
- How many arguments does this call pass, and what are they?
calculate_area(5, 12) Predict the output:
def multiply(a, b): print(a * b) multiply(3, 7) multiply(10, 2) multiply(4, 4)- Write a function called
print_gradethat takes two parameters — a student'snameand theirscore— and prints: "Alex: 85/100". - What happens when you call a function with more arguments than it has parameters? Make a prediction, then try it.
4 The return Statement
So far, all our functions have printed results. Printing is fine for displaying things to a user — but it is not the same as making a value available to the rest of your program. The return statement does that: it sends a value back to whoever called the function, so the result can be stored in a variable, passed to another function, or used in a calculation.
This is one of the most important distinctions in programming, and one of the most commonly confused. Let's deal with it directly.
print() puts text on the screen. The value disappears the moment it is displayed — your program cannot use it for anything else.return sends a value back to the caller. The caller can store it, calculate with it, pass it to another function, or use it in a condition. Nothing is displayed unless you explicitly print() the returned value.
A function that only print()s its result is like a calculator that shows the answer on a screen but has no way to send the number to another machine. A function that returns its result is like a calculator that outputs the answer as a signal that other devices can receive and use.
4.1 A Function Without return
def add(a, b):
print(a + b) # displays the result — but does not return it
result = add(3, 4) # add() runs and prints 7
print(result) # prints: NoneThe function printed 7 to the screen — but when we tried to store the result in a variable, we got None. That is because a function with no return statement automatically returns None (Python's way of saying "nothing"). The value 7 was displayed and then lost.
4.2 The Same Function With return
def add(a, b):
return a + b # sends the result back to the caller
result = add(3, 4) # the returned value (7) is stored in result
print(result) # prints: 7
# Now we can use the result:
doubled = add(3, 4) * 2
print(doubled) # prints: 14The function no longer displays anything — it just does the calculation and hands the result back. The caller decides what to do with it.
4.3 return Ends the Function
The moment Python hits a return statement, the function stops — immediately. Any lines after return are never reached:
def check_pass(score):
if score >= 60:
return "Pass" # function ends here if score >= 60
return "Fail" # only reached if score < 60
print(check_pass(85)) # Pass
print(check_pass(40)) # FailThis is a clean, readable pattern: check the first condition and return early if it matches; otherwise fall through to the next return. You will see this in almost every professional codebase.
4.4 Using return Values in Practice
The real power of return is that it lets functions feed into each other:
def calculate_percentage(score, total):
return round(score / total * 100, 1)
def get_grade(percentage):
if percentage >= 90:
return "A"
elif percentage >= 80:
return "B"
elif percentage >= 70:
return "C"
elif percentage >= 60:
return "D"
else:
return "F"
# Using both functions together:
score = 37
total = 50
pct = calculate_percentage(score, total) # returns 74.0
grade = get_grade(pct) # returns "C"
print(f"Score: {score}/{total} ({pct}%) — Grade: {grade}")Each function does one job. The output of one becomes the input of another. This is how real programs are built — not one enormous block of code, but small, composable functions that do one thing well.
If you call a function and store the result in a variable, but that variable ends up as None, the almost certain cause is that your function uses print() instead of return. Ask yourself: "Does this function need to display something, or does it need to send a value back?" If the answer is "send a value back," use return.
- What is the difference between
print()andreturninside a function? Give a one-sentence answer for each. What does this program print? Trace it carefully — there may be a surprise.
def double(n): print(n * 2) result = double(5) print("Result is:", result)Fix the function so the program prints
Result is: 10correctly:def double(n): print(n * 2) result = double(5) print("Result is:", result)- Write a function called
area_of_rectanglethat takeswidthandheightas parameters and returns the area. Then write code that calls it and prints: "Area: 48" (for width=6, height=8). - What value does Python return from a function that has no
returnstatement?
5 Variable Scope
Scope is the set of rules that governs where a variable can be seen and used. This is one of the most important concepts in functions — and one of the most frequent sources of bugs and confusion for beginners. Read this section carefully.
Imagine every function has its own private room with its own whiteboard. Variables created inside that function are written on that room's whiteboard. When the function finishes, the room is locked — the whiteboard is wiped clean. Code outside the function cannot see the whiteboard inside it, and code inside the function cannot see the whiteboard in another function's room.
The hallway outside all the rooms is the global space. Variables written there can be seen from anywhere. But a variable written only inside a room cannot be seen from the hallway.
5.1 Local Scope
A variable created inside a function is called a local variable. It exists only while the function is running, and it can only be used inside that function. Once the function returns, the local variable disappears.
def calculate_bonus(salary):
bonus = salary * 0.1 # bonus is a LOCAL variable
return bonus
calculate_bonus(50000)
print(bonus) # NameError: name 'bonus' is not definedbonus was created inside calculate_bonus(). Outside the function, it does not exist. Python raises a NameError because from the outside world, bonus was never created.
5.2 Global Scope
A variable created outside all functions is called a global variable. It can be read from anywhere in the file — including inside functions.
TAX_RATE = 0.2 # global variable — readable everywhere
def calculate_tax(price):
tax = price * TAX_RATE # reading TAX_RATE from global scope — this is fine
return tax
print(calculate_tax(100)) # 20.0
print(calculate_tax(250)) # 50.0Reading a global variable from inside a function is perfectly normal and useful — especially for constants like TAX_RATE or MAX_SCORE that should not change. The convention is to name constants in ALL_CAPS to signal that they are global and fixed.
5.3 The Crucial Rule: Functions Cannot Modify Globals (by default)
Reading a global variable is fine. But trying to change a global variable from inside a function does not work the way you might expect. Python treats any variable you assign inside a function as a new local variable — even if a global with the same name already exists:
score = 0 # global variable
def add_points(points):
score = score + points # Python creates a LOCAL score — does not touch the global!
print("Inside function, score:", score)
add_points(10)
print("Outside function, score:", score) # still 0 — unchanged!This is one of the most common beginner bugs. When you assign to a variable inside a function, Python creates a brand-new local variable with that name — it does not touch the global. The global and the local are completely separate, even though they have the same name.
The fix is almost never to use the global keyword (which is an advanced feature with downsides). The correct fix is to use return: have the function calculate the new value and return it, then update the variable outside:
score = 0
def add_points(current_score, points):
return current_score + points # return the new value
score = add_points(score, 10) # update the global by reassigning it
print(score) # 10 — correct!5.4 Local Variables in Different Functions Are Separate
Two different functions can both use a variable named result or total — they are completely independent. Each function has its own private scope:
def calculate_area(width, height):
result = width * height # this "result" belongs to calculate_area
return result
def calculate_perimeter(width, height):
result = 2 * (width + height) # this "result" is a DIFFERENT variable
return result
area = calculate_area(4, 6) # 24
perimeter = calculate_perimeter(4, 6) # 20
print(area, perimeter)Both functions use a variable called result. They do not interfere with each other at all — each function's result lives in its own private scope.
5.5 A Visual Summary of Scope
| Variable type | Where it is created | Where it can be read | Where it can be modified |
|---|---|---|---|
| Local | Inside a function. | Only inside that function. | Only inside that function. Disappears when the function returns. |
| Global | Outside all functions, at the top level of the file. | Anywhere in the file, including inside functions. | Safely only outside functions. Inside a function, assigning creates a new local instead. |
Functions should get their input through parameters and send their output back through return. They should not silently reach out and read or modify global variables (except named constants). A function that follows this rule is self-contained — you can understand it completely by reading only its own code, without hunting through the rest of the program.
- What is a local variable? What happens to it when the function returns?
- What is a global variable? Can a function read a global variable?
This program has a scope bug. Identify it and explain what is actually happening:
total = 100 def apply_discount(amount): total = total - amount print("New total:", total) apply_discount(20) print("Final total:", total)- Fix the program above so that
totalis correctly updated. Usereturn— not theglobalkeyword. - Two functions both have a local variable called
count. Can they interfere with each other? Explain why or why not.
6 Putting It All Together
Here is a complete program that uses everything from this article: well-named functions, parameters, return values, and clean scope. Read it carefully — notice how each function does exactly one job, takes its input through parameters, and sends its result back through return.
PASSING_SCORE = 60 # global constant — readable everywhere
def calculate_percentage(score, total):
"""Returns score as a percentage of total, rounded to 1 decimal place."""
return round(score / total * 100, 1)
def get_grade(percentage):
"""Returns a letter grade for a given percentage."""
if percentage >= 90:
return "A"
elif percentage >= 80:
return "B"
elif percentage >= 70:
return "C"
elif percentage >= 60:
return "D"
else:
return "F"
def is_passing(percentage):
"""Returns True if the percentage meets the passing threshold."""
return percentage >= PASSING_SCORE
def print_report(name, score, total):
"""Prints a formatted report card line for one student."""
pct = calculate_percentage(score, total)
grade = get_grade(pct)
status = "PASS" if is_passing(pct) else "FAIL"
print(f" {name:<12} {score}/{total} ({pct}%) Grade: {grade} [{status}]")
# ── Main program ──────────────────────────────────────────────────
students = [
("Alice", 43, 50),
("Bob", 28, 50),
("Carol", 47, 50),
("Dan", 31, 50),
]
print("=== End of Term Report ===")
for name, score, total in students:
print_report(name, score, total)What to notice:
- Each function has a clear, single purpose — the name describes exactly what it does.
- Every function gets its inputs through parameters and sends results back through
return. None of them modify global variables. PASSING_SCOREis a global constant — named in ALL_CAPS, read insideis_passing(), never modified.print_report()calls three other functions and uses their return values — this is functions feeding into each other.- The main program loop is clean and short because all the logic lives in the functions.
7 Common Mistakes to Watch For
| Mistake | What it looks like | What actually happens | Fix |
|---|---|---|---|
| Calling before defining | greet() appears before def greet(): | NameError — Python does not know the name yet. | Move all def statements above the code that calls them. |
Using print instead of return | Function prints a result; caller stores it in a variable. | Variable is None. Displayed value cannot be reused. | Replace print(result) inside the function with return result. |
| Forgetting to use the return value | calculate_tax(price) called but result not stored. | Function runs, calculates correctly, result disappears. | Write tax = calculate_tax(price) to capture the result. |
| Trying to modify a global inside a function | score = score + 10 inside a function expecting to update the global. | Creates a new local score; global is unchanged. May also raise UnboundLocalError. | Pass the value in as a parameter, update it with return, and reassign outside. |
| Wrong number of arguments | def add(a, b): called as add(5) | TypeError: missing 1 required positional argument | Match the number of arguments to the number of parameters, or add a default value. |
| Expecting a local variable to exist outside the function | Using a variable after the function returns, expecting it still to exist. | NameError — local variables are destroyed when the function ends. | Use return to send the value out; store it in a variable outside the function. |
8 Final Check: The Big Picture
A function is a named, reusable block of code. You define it once with def and call it whenever you need it. Parameters are the placeholders in the definition; arguments are the actual values you pass when calling. return sends a value back to the caller — it is not the same as print(). Local variables exist only inside their function and disappear when it returns; global variables exist outside all functions. Functions should take input through parameters and send output through return — this keeps them self-contained, readable, and easy to fix.
- Explain in plain English what a function is and why it is more useful than copying and pasting code.
Trace this program completely. Write down exactly what is printed and in what order.
def square(n): return n * n def cube(n): return n * n * n x = 3 print(square(x)) print(cube(x)) print(square(x) + cube(x))This program is supposed to print
The total is: 150but it printsThe total is: None. Find the bug and fix it.def add_all(a, b, c): total = a + b + c print(total) result = add_all(40, 60, 50) print("The total is:", result)Explain what will happen when this code runs. What error occurs and why?
def calculate(x): answer = x * 10 return answer calculate(5) print(answer)Write a complete program with two functions:
celsius_to_fahrenheit(c)— takes a Celsius temperature and returns the Fahrenheit equivalent. (Formula:F = C * 9/5 + 32)describe_temperature(f)— takes a Fahrenheit value and returns a string:"Freezing"if below 32,"Cold"if below 60,"Warm"if below 85, and"Hot"otherwise.
Then write a main section that asks the user for a Celsius temperature, converts it, and prints both the Fahrenheit value and the description.
9 Quick-Reference Summary
| Concept | Syntax / Example | Key rule |
|---|---|---|
| Define a function | def my_function(): | Registers the name and body. Does not run the code yet. |
| Call a function | my_function() | Runs the body. Must be called after it is defined. |
| Parameter | def greet(name): | Placeholder in the definition. A slot waiting for a value. |
| Argument | greet("Alex") | The actual value passed in at call time. |
| Default parameter | def greet(name, greeting="Hello"): | Used when the caller does not provide that argument. |
return | return result | Sends a value back to the caller. Ends the function immediately. |
No return | (function ends without return) | Function automatically returns None. |
| Local variable | Created inside a function. | Exists only during that function call. Invisible outside. |
| Global variable | Created outside all functions. | Readable everywhere. Assigning inside a function creates a new local instead. |
| Global constant | MAX_SCORE = 100 | Convention: ALL_CAPS. Read inside functions, never modified. |