DAY 5 - Functions: Writing Reusable Code
5.1 Why Functions Matter
Imagine you need to calculate a discounted price in twenty different places in your programme. Without functions, you copy the same logic twenty times. When the discount rate changes, you update it twenty times - and probably miss one. This violates the DRY principle: Don't Repeat Yourself.
A function is a named, reusable block of code. You write it once and call it wherever you need it.
5.2 Defining and Calling Functions
def greet():
print("Hello! Welcome to Luxley Digital College.")
greet() # calling the function
greet() # calling it againThe keyword def declares a function. The function name follows the same naming rules as variables (snake_case). The body is indented.
5.3 Parameters and Arguments
Parameters are variables listed in the function definition. Arguments are the actual values passed when calling the function.
def greet_user(name): # 'name' is a parameter
print(f"Hello, {name}!")
greet_user("Alice") # 'Alice' is an argument
greet_user("Bob")Multiple parameters
def calculate_bmi(weight_kg, height_m):
bmi = weight_kg / (height_m ** 2)
return round(bmi, 1)
result = calculate_bmi(70, 1.75)
print(f"BMI: {result}") # BMI: 22.95.4 The return Statement
return sends a value back to the caller. Without return, a function implicitly returns None.
def add(a, b):
return a + b
total = add(3, 7) # total = 10
print(total + 5) # 15 - we can use the returned valueA function can return multiple values as a tuple:
def min_max(numbers):
return min(numbers), max(numbers)
low, high = min_max([4, 1, 9, 2, 7])
print(f"Min: {low}, Max: {high}") # Min: 1, Max: 95.5 Default Parameters
You can provide default values for parameters. If the caller does not provide that argument, the default is used:
def power(base, exponent=2):
return base ** exponent
print(power(5)) # 25 - uses default exponent of 2
print(power(5, 3)) # 125 - overrides defaultParameters with defaults must come after parameters without defaults:def func(a, b=2) is valid; def func(a=1, b) is not.
5.6 Keyword Arguments
When calling a function, you can specify arguments by name. This makes code more readable and allows you to pass arguments in any order:
def create_profile(name, age, city="London"):
print(f"{name}, {age}, {city}")
create_profile("Alice", 28)
create_profile(age=32, name="Bob", city="Manchester")
create_profile("Charlie", city="Edinburgh", age=25)5.7 *args and **kwargs
Sometimes you do not know in advance how many arguments a function will receive.
*args - variable positional arguments
The * collects extra positional arguments into a tuple:
def total(*numbers):
return sum(numbers)
print(total(1, 2, 3)) # 6
print(total(10, 20, 30, 40)) # 100**kwargs - variable keyword arguments
** collects extra keyword arguments into a dictionary:
def print_info(**details):
for key, value in details.items():
print(f"{key}: {value}")
print_info(name="Alice", age=28, city="London")You will encounter *args and **kwargs frequently in library source code and documentation.
5.8 Variable Scope
A variable defined inside a function exists only within that function. This is called local scope:
def my_function():
local_var = "I only exist inside this function."
print(local_var)
my_function()
print(local_var) # NameError - local_var does not exist hereVariables defined outside any function have global scope and can be read (but not modified) inside a function:
company = "Luxley Digital"
def show_company():
print(company) # reads the global variable - fine
show_company() # Luxley DigitalTo modify a global variable inside a function, use the global keyword - but do this sparingly, as it makes code harder to reason about:
counter = 0
def increment():
global counter
counter += 1
increment()
increment()
print(counter) # 25.9 Lambda Functions
A lambda is a small, anonymous function defined in a single line. It is used for short operations that are passed as arguments to other functions:
# Regular function
def square(x):
return x ** 2
# Equivalent lambda
square = lambda x: x ** 2
print(square(5)) # 25Lambdas shine when used with sorted(), map() and filter():
students = [("Alice", 85), ("Bob", 92), ("Charlie", 78)]
# Sort by score (second element of each tuple)
sorted_students = sorted(students, key=lambda s: s[1], reverse=True)
print(sorted_students)
# [('Bob', 92), ('Alice', 85), ('Charlie', 78)]5.10 Docstrings
A docstring is a string at the very start of a function that documents what it does. It is not a comment - it is accessible at runtime via help().
def calculate_discount(price, discount_pct):
"""
Calculate the final price after applying a percentage discount.
Parameters:
price (float): The original price.
discount_pct (float): Discount percentage (e.g., 10 for 10%).
Returns:
float: The discounted price, rounded to 2 decimal places.
"""
return round(price * (1 - discount_pct / 100), 2)
help(calculate_discount) # displays the docstringWrite docstrings for every function you create from this point forward. Your future self will thank you.
✏️ Day 5 Exercises
Exercise 5.1 - Temperature Converter
Write a function celsius_to_fahrenheit(celsius) and a function fahrenheit_to_celsius(fahrenheit). Then write a third function convert_temperature(value, unit) where unit is either "C" or "F", and it calls the appropriate converter. Test it with 20°C, 98.6°F, 0°C, 212°F.
Formulae: F = (C × 9/5) + 32 and C = (F − 32) × 5/9.
Hint: use an if/elif in convert_temperature to decide which sub-function to call. Round results to 2 decimal places.
Exercise 5.2 - Basic Data Cleaning Function
Write a function clean_string(text) that:
- Strips leading and trailing whitespace.
- Converts to Title Case.
- Replaces multiple internal spaces with a single space.
Then write a second function clean_dataset(data) that takes a list of strings and returns a new list with every string cleaned. Test data:
raw = [" alice smith ", "BOB JONES", " charlie brown "]Hint: for step 3, you can use " ".join(text.split()) - .split() without arguments splits on any whitespace and ignores extras, then " ".join(...) reassembles with single spaces.
Exercise 5.3 - Statistics Functions
Without using any imports, write the following functions from scratch:
mean(numbers)- returns the arithmetic mean.variance(numbers)- returns the population variance.std_dev(numbers)- returns the population standard deviation (usex ** 0.5).
Formulae:
- Mean: sum of values ÷ count
- Variance: mean of squared differences from the mean
- Standard deviation: √variance
Hint: variance can call mean. std_dev can call variance.
Exercise 5.4 - Challenge: Flexible Report Generator
Write a function generate_report(**data) that accepts any number of keyword arguments and prints them as a formatted report. Include a separator line, a title if a "title" key is present, and all other key-value pairs below it.
Example call:
generate_report(title="Q1 Sales", region="North", total=125000, growth="12%")Expected output:
==============================
REPORT: Q1 Sales
------------------------------
region : North
total : 125000
growth : 12%
==============================Hint: use data.pop("title", "Untitled") to extract the title before looping over the remaining items.
📌 Day 5 Summary
| Concept | What you learned |
|---|---|
def | Declare a function |
| Parameters & arguments | Inputs to a function |
return | Send a value back to the caller |
| Default parameters | Fallback values when arguments are omitted |
| Keyword arguments | Pass arguments by name |
*args / **kwargs | Variable-length argument lists |
| Scope | Local vs global variables |
| Lambda | Anonymous, single-expression functions |
| Docstrings | Documentation embedded in the function |
Tomorrow you will read and write files, handle errors gracefully, and learn to use Python's standard library - turning your programmes into tools that interact with the real world.