Defining Functions, Parameters, Return
Functions are the single biggest upgrade in your coding life. Before them: one long script, top to bottom. After them: named, reusable, testable blocks — the difference between a pile of code and a *program*.
The problem functions solve
# The same greeting logic, three times...
name1 = "Aarav"
print(f"Hello, {name1}! Welcome back.")
name2 = "Diya"
print(f"Hello, {name2}! Welcome back.")
name3 = "Kabir"
print(f"Hello, {name3}! Welcome back.")Change the greeting format? Edit three places. Miss one? Bug. Functions collapse repetition into one definition:
def greet(name):
print(f"Hello, {name}! Welcome back.")
greet("Aarav")
greet("Diya")
greet("Kabir")Anatomy of a function
def greet(name): # def + name + parameters + colon
"""Say hello to someone.""" # docstring (optional but pro)
message = f"Hello, {name}!" # the body — indented block
return message # send the result back
result = greet("Aarav") # calling it
print(result) # Hello, Aarav!Four parts:
return — the whole point
return sends a value back to whoever called the function — and ends the function immediately:
def add(a, b):
return a + b
total = add(5, 3) # add runs, returns 8, total holds 8
print(total) # 8
print(add(10, 20) * 2) # returned values work anywhere: 60The crucial distinction — printing vs returning:
def bad_add(a, b):
print(a + b) # DISPLAYS 8, but returns...
x = bad_add(5, 3)
print(x) # None! — printing is not returningA function without a return (or with bare return) gives back None. If your function's result "disappears," check: did you return it, or just print it? This is the #1 beginner function bug.
Multiple parameters — order matters
def introduce(name, age, city):
print(f"{name}, {age}, from {city}")
introduce("Aarav", 16, "Delhi") # positional: order decides
introduce(city="Mumbai", name="Diya", age=15) # keyword: names decidePositional arguments match by order. Keyword arguments match by name — order stops mattering, and readability jumps.
The docstring habit
def calculate_tip(bill, percent):
"""Return the tip amount for a bill at a given percentage."""
return bill * percent / 100One line: what it returns, from what inputs. help(calculate_tip) now shows it — your future self will thank present-you.
Functions compute — they don't ask
A subtle design principle: keep input/output OUT of calculation functions:
# ❌ mixed concerns — can't reuse without a human typing
def get_bmi():
w = float(input("Weight: "))
h = float(input("Height: "))
print(w / h ** 2)
# ✅ pure function — testable, reusable anywhere
def calculate_bmi(weight, height):
return weight / height ** 2
# the UI wraps around it
print(calculate_bmi(70, 1.75))The pure version works from user input, files, APIs, or tests — because it only *computes*.
Common Errors & Fixes
return. Printing ≠ returning.def only *creates* the function. Nothing runs until you call it.✅ Checkpoint
print and return inside a function? *(Display vs send back — print returns None)*introduce("Aarav", 16, "Delhi") — what are the three values called? *(Positional arguments)*Next: default and keyword arguments — functions that adapt.