Lesson 7 of 16 · Level 2: Intermediate
Python Functions: Parameters, return and Modules
- ⏱ 13 min read
- ❓ 4 review questions
- ✍️ Exercise with solution
In this lesson
- Define functions with parameters and default values.
- Return results with return and understand variable scope.
- Document functions with docstrings and type hints.
- Organize a program into functions and use the standard library.
- Build a pricing calculator for a small business with reusable functions.
As your programs grow, you start seeing repeated code that's hard to follow. Functions solve both problems: they group a set of instructions under a name so you can reuse them as often as you like. They're the single most important tool for writing clean code, and everything you'll do later in Python, from pandas to machine learning, is built on them.
() are what run itreturn gives back: the source of many bugsDefining and calling a function
def greet():
print("Hello!")
print("Welcome to the system")
greet()
greet()
defdefines the function, followed by its name (in snake_case), parentheses and a colon.- The body is indented.
- The function does nothing until you call it by writing its name with parentheses. Here it runs twice, so the two lines print twice.
Parameters: input data
def greet(name):
print(f"Hello, {name}!")
greet("Jane")
greet("Carlos")
A parameter is the variable in the definition (name); an argument is the value you pass when you call it ("Jane").
Several parameters and default values
def final_price(price, tax=0.0825, discount=0):
base = price * (1 - discount)
return base * (1 + tax)
print(round(final_price(100), 2)) # 108.25
print(round(final_price(100, discount=0.1), 2)) # 97.42
print(round(final_price(100, tax=0.06), 2)) # 106.0
Parameters with default values are optional and must come after the required ones. When calling, you can name the arguments (discount=0.1): the code reads better and you don't depend on their order. (The second result is 97.42, not 97.43, because 97.425 is stored in binary as 97.42499…; that's lesson 1's float quirk again.)
1. With def greet(name, greeting="Hello"): return f"{greeting}, {name}", what does greet("Jane") return?
If you don't pass a parameter that has a default value, Python uses the default.
return: handing back a result
The difference between print and return is fundamental:
- What happens to the value
- You see it in the terminal, but it's gone: you can't store it or add to it.
- The function returns
None- Use it for
- Telling the user something: receipts, messages, reports.
- What happens to the value
- It goes back to the caller, which can store it or use it in another calculation.
- The function returns
- That value (and stops right there).
- Use it for
- Any calculation another part of the program needs.
def rectangle_area(width, height):
return width * height
a = rectangle_area(4, 5)
total = rectangle_area(4, 5) + rectangle_area(2, 3)
print(a, total) # 20 26
A function without return returns None. And return ends the function immediately, which allows early exits:
def divide(a, b):
if b == 0:
return None
return a / b
You can return several values at once (technically, a tuple, which you then unpack):
def stats(numbers):
return min(numbers), max(numbers), sum(numbers) / len(numbers)
lowest, highest, average = stats([4, 8, 15, 16, 23, 42])
print(average) # 18.0
2. What's the difference between return and print inside a function?
A function that only prints returns None: you can't store or add up its result.
Variable scope
Variables created inside a function are local: they only exist while the function runs.
def calculate():
result = 10 * 2
return result
calculate()
print(result) # NameError: result doesn't exist outside the function
The right way to get a value out of a function is return, not global variables. Functions that depend only on their parameters and return a result are easier to understand, test and reuse.
3. Can you use a variable outside the function where you created it?
Local variables only exist while the function runs. Whatever you need outside, send back with return.
Documenting with docstrings and type hints
Here's a function every car buyer would like: the monthly payment on an auto loan, using the standard amortization formula U.S. lenders use with an APR.
def loan_payment(amount: float, apr: float, months: int) -> float:
"""Return the fixed monthly payment on an amortizing loan.
apr: annual percentage rate as a decimal, e.g. 0.069 for 6.9%.
"""
monthly_rate = apr / 12
return amount * monthly_rate / (1 - (1 + monthly_rate) ** -months)
print(round(loan_payment(25_000, 0.069, 60), 2)) # 493.85
- The text in triple quotes is the docstring: it explains what the function does. VS Code shows it when you hover over the function name.
amount: floatand-> floatare type hints: they state the expected types. Python doesn't enforce them, but they help your editor and anyone reading the code.
Over 60 months, that $25,000 loan costs $29,631.08 in total, or $4,631.08 in interest. If comparing loans by APR interests you, see the lesson on credit scores and loans in our personal finance course.
Standard library functions: import
Python ships with hundreds of ready-to-use modules. You import them at the top of the file:
import math
import random
from datetime import date
print(math.sqrt(144)) # 12.0
print(math.ceil(4.35)) # 5 (rounds up)
print(random.choice(["a", "b", "c"]))
print(date.today()) # today's date
age_in_days = (date.today() - date(1996, 5, 14)).days
Remember the savings exercise in lesson 2, where 4.35 months really meant 5? math.ceil() is the tool for that. Beyond the standard library there are thousands of external libraries you install with pip from the terminal (pip install pandas). They're the reason Python dominates data, web development and AI, and you'll set them up properly in lesson 10.
Organizing a program with functions
A well-organized program reads like a table of contents. Compare this skeleton with a 100-line block of code:
def ask_number(prompt):
return float(input(prompt))
def calculate_total(subtotal, tax=0.0825):
return subtotal * (1 + tax)
def print_receipt(subtotal, total):
print(f"Subtotal: ${subtotal:,.2f}")
print(f"Total: ${total:,.2f}")
def main():
subtotal = ask_number("Subtotal: ")
total = calculate_total(subtotal)
print_receipt(subtotal, total)
if __name__ == "__main__":
main()
The last line means "run main() only if this file is run directly." That way another program can import calculate_total without launching the whole program. Small functions with a single job are also much easier to test automatically, which you'll do with pytest in lesson 10.
Real case: pricing for a home bakery
Maya sells baked goods from her home bakery in Sacramento, California, and used to price by gut feeling. She wants 40% of every sale to be profit and to know how much each product earns per month. With three small functions, each with one job, the math becomes clear and reusable:
# Pricing calculator - Maya's Home Bakery (Sacramento, California)
def unit_cost(batch_cost, units_per_batch):
"""Ingredient cost of one unit."""
return batch_cost / units_per_batch
def suggested_price(cost, margin=0.40):
"""Price at which profit is `margin` of the selling price."""
return round(cost / (1 - margin), 2)
def profit(price, cost, units):
return (price - cost) * units
# (name, batch cost in $, units per batch, units sold per month)
products = [
("Cookies", 36.00, 24, 320),
("Cupcakes", 21.60, 12, 150),
("Chocolate cake", 21.00, 1, 25),
]
total = 0
for name, batch_cost, per_batch, sold in products:
cost = unit_cost(batch_cost, per_batch)
price = suggested_price(cost)
p = profit(price, cost, sold)
total += p
print(f"{name:14} cost {cost:5.2f} | price {price:5.2f} | profit/month {p:6.2f}")
print(f"Estimated monthly profit: ${total:,.2f}")
Output:
Cookies cost 1.50 | price 2.50 | profit/month 320.00
Cupcakes cost 1.80 | price 3.00 | profit/month 180.00
Chocolate cake cost 21.00 | price 35.00 | profit/month 350.00
Estimated monthly profit: $850.00
The surprise for Maya: selling just 25 cakes a month earns more than 320 cookies, because each cake leaves $14 of profit. The advantage of functions shows up when she changes strategy: to try a 50% margin on everything, she changes the default value of margin in one place. Notice too that suggested_price calculates the margin on the selling price (cost ÷ 0.60), not on cost. Adding 40% to cost (cost × 1.40), a common pricing mistake, gives a margin of only about 28.6%.
When a function forgets its return
Suppose you wrote print(...) instead of return ... in suggested_price. The price shows up on screen, but the next line crashes:
Traceback (most recent call last):
File "pricing.py", line 5, in <module>
profit = (price - 1.50) * 320
~~~~~~^~~~~~
TypeError: unsupported operand type(s) for -: 'NoneType' and 'float'
When you see 'NoneType' in an error, immediately suspect a function with no return: the variable price received None. Another frequent message is TypeError: profit() missing 1 required positional argument: 'units', which tells you exactly which argument was left out of the call.
4. What appears on screen with this code?
def double(x):
print(x * 2)
r = double(5)
print(r)
The function displays 10 with its print, but it has no return, so it returns None, and that's what gets stored in r.
Common mistakes with functions
Using print inside the function when you need the value later.
Use return and leave the print to the caller.
Calling without parentheses: greet
Without () the function doesn't run; print(greet) shows something like <function greet at 0x...>.
Passing arguments in the wrong order: profit(320, 2.50, 1.50)
Name them when there are several numbers: profit(price=2.50, cost=1.50, units=320).
Calling a function before it's defined in the file.
Python reads top to bottom: define with def first and call afterwards (or put everything inside main()).
80-line functions that ask for input, calculate and print.
Split input, calculation and output into separate functions, like in the bakery example.
If you copy and paste the same block of code twice, turn it into a function. If a function doesn't fit on your screen, it's probably doing too much: split it.
Practice exercise
- Write a function
is_even(n)that returnsTrueorFalse. - Write
convert_currency(amount, rate, fee=0.01)that returns the converted amount minus a 1% fee. - Write
grade_summary(scores)that takes a list and returns a dictionary with the average, the highest and lowest score, and how many passed (60 or higher).
See solution
def is_even(n):
return n % 2 == 0
def convert_currency(amount, rate, fee=0.01):
converted = amount * rate
return converted * (1 - fee)
def grade_summary(scores):
return {
"average": round(sum(scores) / len(scores), 1),
"highest": max(scores),
"lowest": min(scores),
"passed": len([s for s in scores if s >= 60]),
}
print(is_even(10)) # True
print(round(convert_currency(100, 18.50), 2)) # 1831.5
print(grade_summary([55, 72, 90, 61, 48]))
# {'average': 65.2, 'highest': 90, 'lowest': 48, 'passed': 3}
The 18.50 is just a sample rate (say, dollars to Mexican pesos); in lesson 12 you'll fetch real rates from an API.
Summary
def name(parameters):defines a function; it runs when you call it with().returnhands a value back;printonly displays it.- Default values and named arguments make calls clear and flexible.
- Variables inside a function are local; document functions with docstrings and type hints.
- Organize programs into small functions plus a
main()guarded byif __name__ == "__main__":. - An error mentioning
'NoneType'almost always points to a function with noreturn.
Next lesson: Python Files, Error Handling and Your First Project. You'll handle errors with try/except, read and write text, CSV and JSON files, and build a complete expense tracker.
Create your account in 30 seconds, mark your lessons and download your certificate at the end.