After working with variables, conditions and loops, we can now cover functions.
The easiest way I got to understand functions was to think about repeated work. For instance, if I have to calculate VAT for three different prices, I could write the calculation three times. But if I need the same calculation in ten or twenty places, repeating the same code quickly becomes a hassle.
Essentially what I'm getting at is that, a function lets you write the logic once and reuse it.
The problem with repeating code
Without a function, calculating VAT for three items could look like this:
price1 = 1000
vat1 = price1 * 0.16
total1 = price1 + vat1
price2 = 2500
vat2 = price2 * 0.16
total2 = price2 + vat2
price3 = 750
vat3 = price3 * 0.16
total3 = price3 + vat3
The calculation itself isn't complicated. The problem is repetition. If the VAT rate changes from 16% to 14%, we would have to find every place where 0.16 was used and change it.
A function gives you one place to maintain that logic.
def add_vat(price, rate=0.16):
vat = price * rate
total = price + vat
return total
Now it can be reused:
print(f"Item 1: Ksh {add_vat(1000)}")
print(f"Item 2: Ksh {add_vat(2500)}")
print(f"Item 3: Ksh {add_vat(750)}")
If the rate needs to change, we can change the default in the function instead of changing every calculation.
Defining and calling a function
A function starts with def.
For example:
def greet_student(name):
print(f"Good evening, {name}! Welcome to Python Class.")
At this point, the fucntion has been defined. But it doesn't run just because we defined it. A function needs to be called as shown below:
greet_student("Moses")
greet_student("Sylvia")
greet_student("Amina")
The function runs each time you call it, using the name provided. This is one of the main benefits of functions: define the behaviour once, then reuse it with different values.
Functions can have more than one parameter
You can also pass several pieces of information:
def student_info(name, age, track):
print(f"Name: {name}")
print(f"Age: {age}")
print(f"Track: {track}")
print()
student_info("Sylvia", 26, "Data Science")
student_info("Moses", 20, "Data Engineering")
Here, name, age and track are parameters. When you call the function, you provide the actual values.
You can also use keyword arguments to make it clear which value belongs to which parameter:
describe_student(
track="Data Science",
name="Jane",
city="Nyeri"
)
Printing isn't the same as returning
This is a really important distinctions.
Consider:
def add_print(a, b):
print(a + b)
result = add_print(6, 5)
print(result)
The function prints 11, but result becomes None. That's because the function displayed the answer but didn't give the answer back to the program. If we want to use the result later, we need to use return.
def add_return(a, b):
return a + b
Now you can do something with the result:
result = add_return(5, 10) * 2
print(result)
This gives: 30
Functions can contain decisions
A function can also contain conditional logics.
For example, create a function to determine a grade:
def get_grade(score):
if score >= 80:
return "A"
elif score >= 70:
return "B"
elif score >= 60:
return "C"
elif score >= 50:
return "D"
else:
return "F"
The grading logic doesn't need to be repeated every time there is a score. You can use it with a list by adding the below:
scores = [87, 74, 55, 91, 43]
for score in scores:
grade = get_grade(score)
print(f"Score: {score} Grade: {grade}")
This is where functions and loops work together. The loop goes through the scores, while the function handles the grading logic.
Returning multiple values
Functions can return more than one thing. In this case, create a function that analyses a list of scores:
def analyse_score(scores):
total = sum(scores)
average = round(total / len(scores), 1)
highest = max(scores)
lowest = min(scores)
return total, average, highest, lowest
To receive all four results:
scores = [87, 74, 55, 91, 43, 78, 88]
tot, avg, hi, lo = analyse_score(scores)
print(f"Total: {tot}")
print(f"Average: {avg}")
print(f"Highest: {hi}")
print(f"Lowest: {lo}")
The variables: tot, avg, hi, lo, receive the four values returned by the function. This is useful when one piece of logic needs to calculate several results that are related.
Scope: where variables exist
Another important concept is scope. A variable created inside a function is local to that function.
def calculate_fee():
fee = 4000
print(f"This is fee inside function: {fee}")
calculate_fee()
This works because fee exists inside the function.
But: print(fee) outside the function would cause an error because that local variable doesn't exist there.
Another one is global variables:
school = "Nairobi Academy"
def show_school():
print(f"School: {school}")
show_school()
print(f"Also here: {school}")
A function can read the global variable.
So, what happens when a local variable has the same name as a global one:
name = "Nairobi"
def show_local():
name = "Mombasa"
print(f"Inside function: {name}")
show_local()
print(f"Outside function: {name}")
The output is:
Inside function: Mombasa
Outside function: Nairobi
The two name variables are separate.
The global keyword
You can also use the global keyword when a function needed to change a global variable.
For example:
login_count = 0
def login(username):
global login_count
login_count += 1
print(f"{username} logged in. Total logins is {login_count}")
login("Prudence")
login("John")
login("Daniel")
login("Martha")
The counter is shared between the function calls. In practice, I also learned why keeping data local or passing it into functions can often make code easier to manage. global is something to use when it is actually needed, not simply because it is available.
Default arguments
Functions can also have default values.
For example:
def send_message(recipient, message, channel="SMS"):
print(f"Sending to {recipient} via {channel}: {message}.")
If you don't provide a channel: send_message("Brian", "Meeting at 3pm"), Python uses: SMS. But that can be overridden like this:
send_message(
"Moses",
"Your package has arrived",
"WhatsApp"
)
Default arguments are useful when there is a value that will normally stay the same but sometimes needs to be changed.
*args
The parameter *args, allows a function to receive a variable number of arguments.
For example:
def class_summary(teacher, *scores):
print(f"Teacher: {teacher}")
print(f"Students: {len(scores)}")
if scores:
print(f"Average: {sum(scores) / len(scores)}")
print(f"Highest: {max(scores)}")
print()
Then you can call it with different numbers of scores:
class_summary("Mr. David", 78, 85, 91, 65, 72)
class_summary("Ms. Wanjiku", 88, 94, 76)
The teacher is a required parameter, while *scores collects however many scores we provide.
Putting Functions Together: Payslip Generator Program
To understand functions better, I did a simple program which is a Payslip generator. Instead of putting all the salary calculations into one large block of code, we separated the different deductions into their own functions.
def calculate_nhif(gross):
if gross < 6000:
return 150
elif gross < 12000:
return 400
elif gross < 25000:
return 750
else:
return 950
def calculate_nssf(gross):
return min(round(gross * 0.06), 2160)
def calculate_paye(gross):
if gross <= 24000:
return 0
return round((gross - 24000) * 0.16)
def print_payslip(name, gross):
nhif = calculate_nhif(gross)
nssf = calculate_nssf(gross)
paye = calculate_paye(gross)
deductions = nhif + nssf + paye
net = gross - deductions
width = 40
print("=" * width)
print(f" PAYSLIP - {name}")
print("=" * width)
print(f" Gross salary: Ksh {gross:>9,}")
print(f" NHIF: Ksh {nhif:>9,}")
print(f" NSSF: Ksh {nssf:>9,}")
print(f" PAYE: Ksh {paye:>9,}")
print(f" {'─' * 36}")
print(f" NET PAY: Ksh {net:>9,}")
print("=" * width)
print()
#generate payslips for different employees simply by calling the function
print_payslip("Amina Wanjiku", 85000)
print_payslip("Brian Otieno", 28000)
print_payslip("Njeri Kamau", 22000)
Output:
This can probably be seen as a simple and clear example of the importance of functions. Each function has one responsibility. The overall flow becomes easier to follow:
Gross salary
↓
Calculate NHIF
↓
Calculate NSSF
↓
Calculate PAYE
↓
Add deductions
↓
Subtract from gross
↓
Print payslip
If I needed to change the logic for one deduction, I could work on that particular function instead of searching through one huge block of code.
Finally
The biggest thing I took from functions is that they help turn a program into smaller, reusable pieces. Instead of writing the same calculation repeatedly, we can put it inside a function and call it whenever we need it. You can also see that functions can work together with things we have already learned like: if/elif/else, loops, calculations, lists and f-strings.
The payslip generator brought that picure together really well. Each function handled a specific part of the problem, and the final function combined those pieces into something useful.

Top comments (0)