Python Functions Explained — Define, Call, and Reuse Code Like a Pro
Every app you've ever used — Spotify, Instagram, Google Maps — is built on thousands of small, focused jobs that run on demand. When you hit 'Search', something processes your query. When you tap 'Like', something updates a counter. Each of those 'somethings' is a function. Functions are the fundamental building blocks of every serious Python program, and understanding them is the single biggest leap you'll make as a beginner.
Without functions, your code would be a giant wall of repeated instructions. Imagine writing the same 10 lines to calculate a discount every time a user adds something to a cart. Change the discount rule once and you'd have to hunt down every copy. Functions solve this by letting you write that logic once, give it a name, and call it from anywhere. This is the DRY principle — Don't Repeat Yourself — and functions are how Python enforces it.
By the end of this article you'll know how to write your own functions, pass information into them, get results back out of them, and avoid the three mistakes that trip up almost every beginner. You'll also understand the 'why' behind every keyword — so nothing feels like magic memorisation.
What a Function Is and How to Build Your First One
A function is a named block of code that only runs when you call it. Python needs four things to create one: the def keyword (short for 'define'), a name you choose, a pair of parentheses, and a colon. Everything indented underneath that colon is the function's body — the actual work it does.
The def keyword is you telling Python: 'Hey, remember these instructions under this name for later.' Nothing runs yet. It's like writing a recipe on a card and putting it in a drawer. The recipe doesn't cook itself — you have to take it out and follow it. Calling the function is you following the recipe.
Naming matters. Use lowercase letters with underscores for readability — calculate_tax, not CalculateTax or ct. A good function name reads like a verb because it does something. If you can't describe your function in a single short verb phrase, it's probably trying to do too much.
# --- Defining a function --- # 'def' tells Python we're creating a reusable block of code. # 'greet_user' is the name we're giving it. # The colon starts the function body. def greet_user(): # Everything indented here belongs to the function. # This code does NOT run yet — we're just defining it. print("Hey there! Welcome to TheCodeForge.") print("Let's learn Python together.") # --- Calling the function --- # Now we actually RUN the code inside the function. # Just write the function name followed by parentheses. greet_user() # First call greet_user() # Second call — same result, zero extra effort greet_user() # Third call — see how reuse works?
Let's learn Python together.
Hey there! Welcome to TheCodeForge.
Let's learn Python together.
Hey there! Welcome to TheCodeForge.
Let's learn Python together.
Passing Information In — Parameters and Arguments Demystified
A function with no inputs is like a vending machine with only one button. Useful, but limited. Parameters let you feed information into a function so it can work with different data each time — making it dramatically more powerful.
A parameter is the placeholder name inside the function definition. An argument is the real value you pass when you call the function. People use these words interchangeably in conversation, but knowing the distinction will save you during code reviews and interviews.
You can have as many parameters as you need, separated by commas. Python also supports default parameter values — if a caller doesn't pass an argument, the parameter falls back to its default. This makes your functions flexible without requiring the caller to always provide every piece of information. Think of it like a coffee order: if you don't specify milk, the barista uses the default (whole milk). You can always override it, but you don't have to.
# --- Function with parameters --- # 'customer_name' and 'item_ordered' are PARAMETERS. # They're placeholders — they only get real values when the function is called. def confirm_order(customer_name, item_ordered): print(f"Order confirmed for {customer_name}!") print(f"You ordered: {item_ordered}") print("---") # When we call the function, we pass ARGUMENTS — the actual values. # 'Alice' replaces 'customer_name', 'Python Book' replaces 'item_ordered'. confirm_order("Alice", "Python Book") confirm_order("Bob", "Mechanical Keyboard") # --- Default parameter values --- # If 'discount_percent' isn't provided, it defaults to 10. # This makes it optional for the caller. def calculate_final_price(original_price, discount_percent=10): # Calculate the discount amount discount_amount = original_price * (discount_percent / 100) # Subtract the discount from the original price final_price = original_price - discount_amount print(f"Original: ${original_price:.2f}") print(f"Discount: {discount_percent}% (saves ${discount_amount:.2f})") print(f"You pay: ${final_price:.2f}") print("---") # Calling without a discount — uses the default 10% calculate_final_price(100) # Calling WITH a custom discount — overrides the default calculate_final_price(100, 25)
You ordered: Python Book
---
Order confirmed for Bob!
You ordered: Mechanical Keyboard
---
Original: $100.00
Discount: 10% (saves $10.00)
You pay: $90.00
---
Original: $100.00
Discount: 25% (saves $25.00)
You pay: $75.00
---
Getting Information Back Out — The return Statement
So far our functions print things, but printing and returning are completely different. print() just displays text on your screen — the value vanishes. return hands the value back to the caller, where it can be stored in a variable, used in a calculation, or passed into another function.
This is the difference between a calculator that shows you the answer on its screen (useful but temporary) versus one that also writes the answer down on a piece of paper you can use later.
A function stops executing the moment it hits a return statement — anything written after return in the same function won't run. You can also have multiple return statements in a function (usually inside if/else blocks), which lets you return different values based on different conditions. If a function has no return statement at all, Python silently returns None — a special value meaning 'nothing here'.
# --- Basic return --- # This function CALCULATES something and GIVES IT BACK to the caller. # Without 'return', the result would be trapped inside the function forever. def calculate_area_of_rectangle(width, height): area = width * height # Do the calculation return area # Hand the result back to whoever called us # The returned value lands in 'living_room_area' — we can use it anywhere. living_room_area = calculate_area_of_rectangle(5, 4) print(f"Living room area: {living_room_area} square metres") # We can also use the return value directly in an expression. total_area = calculate_area_of_rectangle(5, 4) + calculate_area_of_rectangle(3, 3) print(f"Combined area: {total_area} square metres") # --- Multiple return paths --- # The function returns different values based on a condition. # It stops the moment it hits any 'return' statement. def classify_temperature(celsius): if celsius < 0: return "freezing" # Function exits here if celsius < 0 elif celsius < 15: return "cold" # Function exits here if 0 <= celsius < 15 elif celsius < 25: return "comfortable" # Function exits here if 15 <= celsius < 25 else: return "hot" # Function exits here for everything else # Each call follows a different path through the function. print(classify_temperature(-5)) # freezing print(classify_temperature(10)) # cold print(classify_temperature(20)) # comfortable print(classify_temperature(35)) # hot # --- What happens without return --- # This function prints but does NOT return anything. def print_greeting(name): print(f"Hello, {name}!") # Storing the 'result' of a function with no return statement... result = print_greeting("Maya") print(f"The return value was: {result}") # ...gives you None
Combined area: 29 square metres
freezing
cold
comfortable
hot
Hello, Maya!
The return value was: None
Variable Scope — Why Your Variable Disappears Outside the Function
Scope is one of those concepts that trips up almost every beginner, but the mental model is simple: variables created inside a function live only inside that function. They're born when the function is called and they die when the function returns. The outside world can't see them.
This is called local scope. Variables defined outside all functions live in global scope and can be read from anywhere — but modifying them from inside a function requires the global keyword, which you should generally avoid (it creates hidden dependencies that make code hard to debug).
The right pattern is always: pass data in via parameters, get data out via return. Think of a function as a sealed workshop. You pass raw materials through a hatch (parameters), work is done inside, and finished goods come back out through the hatch (return). You don't need — and shouldn't want — the workshop to secretly rearrange your living room while it works.
# --- Local scope demonstration --- def brew_coffee(bean_type): # 'brewing_temperature' is a LOCAL variable. # It only exists inside this function. brewing_temperature = 93 # degrees Celsius result = f"Brewing {bean_type} at {brewing_temperature}°C" return result cup = brew_coffee("Ethiopian Yirgacheffe") print(cup) # Trying to access 'brewing_temperature' outside the function causes an error. # Uncomment the next line to see: NameError: name 'brewing_temperature' is not defined # print(brewing_temperature) # --- Global vs local --- # 'shop_name' is a GLOBAL variable — defined outside any function. shop_name = "TheCodeForge Coffee" def display_shop_info(): # We can READ global variables from inside a function. print(f"Welcome to {shop_name}") display_shop_info() # --- The right way: pass in, return out --- # Instead of modifying globals, we pass data in and get new data back. def apply_loyalty_discount(original_price, loyalty_points): # 'discount_rate' is local — it can't leak out and cause side effects. discount_rate = min(loyalty_points * 0.01, 0.30) # cap at 30% discounted_price = original_price * (1 - discount_rate) return round(discounted_price, 2) final_price = apply_loyalty_discount(50.00, 15) print(f"Price after loyalty discount: ${final_price}")
Welcome to TheCodeForge Coffee
Price after loyalty discount: $42.5
| Aspect | print() inside a function | return from a function |
|---|---|---|
| What it does | Displays text on screen right now | Hands a value back to the caller |
| Is the value reusable? | No — it disappears after display | Yes — store it in a variable and use it anywhere |
| Can you do maths with it? | No — print produces no usable value | Yes — returned numbers/strings can be used in expressions |
| What you get back | None (silently) | Whatever you put after 'return' |
| Good for | Debugging, user messages | Calculations, data transformation, logic results |
| Real-world analogy | A cashier reading your total aloud | A cashier writing your total on a receipt you keep |
🎯 Key Takeaways
defdefines a function — it doesn't run the code. You must call the function by name with parentheses to actually execute it.- Parameters are placeholders in the definition; arguments are the real values you pass at call time. Default parameters make arguments optional.
returnhands a value back to the caller so it can be stored and reused — it's completely different fromprint(), which only displays and discards.- Variables created inside a function are local — they don't exist outside it. Pass data in via parameters and out via
returninstead of relying on global variables.
⚠ Common Mistakes to Avoid
- ✕Mistake 1: Confusing print() with return — A function that only uses print() seems to 'work' during testing because you can see output, but when you try to store or use the result (
result = my_function()) you getNone. Fix: ask yourself 'do I need this value later?' If yes, usereturn, notprint(). - ✕Mistake 2: Calling a function before defining it — Python reads files top to bottom. If you call
calculate_total()on line 3 and define it on line 10, you'll getNameError: name 'calculate_total' is not defined. Fix: always define your functions at the top of the file (or at least before the first call). - ✕Mistake 3: Putting a default argument before a non-default argument — Writing
def register_user(role='viewer', username)causes aSyntaxError: non-default argument follows default argument. Python can't figure out which argument is which when you call it. Fix: always put parameters WITH defaults AFTER parameters WITHOUT defaults —def register_user(username, role='viewer').
Interview Questions on This Topic
- QWhat's the difference between a parameter and an argument in Python? Can you give a concrete example?
- QWhat does a Python function return if it has no explicit return statement, and where could that cause a silent bug in real code?
- QWhat is variable scope, and what's the practical problem with using the 'global' keyword inside a function — can you think of an alternative approach?
Frequently Asked Questions
What is a function in Python and why do we use it?
A function is a named, reusable block of code that performs a specific task. We use functions to avoid repeating the same code in multiple places (DRY principle), to break large problems into smaller manageable pieces, and to make code easier to test and maintain. You define it once with def and call it as many times as you need.
What is the difference between return and print in a Python function?
print() displays a value on screen but the value is lost after that — you can't store or reuse it. return sends the value back to whatever code called the function, where it can be stored in a variable, used in a calculation, or passed elsewhere. If you ever find yourself storing the result of a function (result = my_func()) and getting None, your function is probably using print instead of return.
Can a Python function return more than one value?
Yes — Python lets you return multiple values by separating them with commas: return width, height. Python secretly wraps them in a tuple, so on the calling side you can unpack them cleanly: w, h = get_dimensions(). This is a very common and idiomatic Python pattern used in professional codebases all the time.
Written and reviewed by senior developers with real-world experience across enterprise, startup and open-source projects. Every article on TheCodeForge is written to be clear, accurate and genuinely useful — not just SEO filler.