A function packages up a piece of logic so you can reuse it instead of copying and pasting the same code everywhere.

Defining and calling a function

def greet(name):
    return "Hello, " + name + "!"

message = greet("Aman")
print(message)   # Hello, Aman!

Default arguments

You can give a parameter a default value, making it optional when calling the function:

def greet(name, greeting="Hello"):
    return greeting + ", " + name + "!"

print(greet("Riya"))                # Hello, Riya!
print(greet("Riya", "Welcome"))     # Welcome, Riya!

Why functions matter beyond "avoiding repetition"

Breaking a program into small functions, each doing one clear thing, makes code far easier to test and debug - if something is wrong, you can check each function in isolation instead of hunting through one giant block of code.

def calculate_total(prices):
    return sum(prices)

def apply_discount(total, percent):
    return total - (total * percent / 100)

cart = [499, 999, 199]
total = calculate_total(cart)
final_price = apply_discount(total, 10)
print(f"You pay: Rs.{final_price}")

Variable scope

A variable created inside a function only exists inside that function - it does not leak out to the rest of your program:

def set_score():
    score = 100   # local to this function

set_score()
print(score)   # NameError - score does not exist here