Programming Paradigms
In short: a paradigm is a style for organising code toward the same
goal, not a different language. Imperative code says exactly what to do,
step by step, changing variables as it goes (for loops, running totals).
Object-oriented code bundles state and the behaviour that changes it
into one object, so many pieces of code can share it safely (a class with
methods). Functional code builds a result by transforming data with
functions that don't change anything outside themselves — the map,
filter, reduce and comprehensions from this section's earlier lessons.
Python never forces one style. Most real code mixes all three, picked by
what each part of the job actually needs.

Average price above a threshold, three ways
A café wants the average price of items over ₹20. The same answer, three styles:
None of the three is "more correct". The imperative version is the most
obvious for a quick script. The OOP version earns its keep when tracker
needs to keep living — receiving more prices over time, from different parts
of a program. The functional version is the shortest when the data is
already sitting in a list and nothing needs to remember state afterward.
A cart that remembers vs. a rule that doesn't
Some jobs genuinely need memory across many calls — that's OOP's natural home. Others are a single, self-contained transformation — that's functional's home. Mixing them by job, not by habit, is the actual skill:
Cart has to be an object — it accumulates items one add() at a time,
across however many calls the checkout flow makes. apply_discount has no
reason to be a method on anything: give it the same two arguments twice, and
it returns the same answer twice. That's what "pure" means, and it's why a
plain function is the right shape for it.
seen_totals = []
def apply_discount_impure(subtotal, code):
rates = {"SAVE10": 0.10}
result = round(subtotal * (1 - rates.get(code, 0.0)), 2)
seen_totals.append(result) # a side effect: changes something outside
return result
apply_discount_impure(100, "SAVE10")
apply_discount_impure(100, "SAVE10")
print(len(seen_totals))When "functional" hides a bug
Chaining filter/map/comprehensions is functional style, but style isn't
an excuse to compress everything into one unreadable line — a lesson this
section already made about comprehensions:
orders = [{"price": 90, "cancelled": False}, {"price": 40, "cancelled": True}]
total = sum(o["price"] for o in filter(lambda o: not o["cancelled"], [o for o in orders if o["price"] > 0]))
print(total)Functional tools are for clarity, not for proving you can nest four of them. If untangling a one-liner takes longer than writing a loop would, write the loop — or split it into two readable lines.
Quick practice
def total_score(scores):
total = 0
for s in scores:
total += s
return totalWhich paradigm best describes this function's STYLE?
class Counter:
def __init__(self):
self.n = 0
def bump(self):
self.n += 1
return self.nWhich paradigm best describes this code's STYLE?
def shout(word):
return word.upper() + "!"
print(list(map(shout, ["go", "run"])))🎯 Your turn
Write functional_average(nums, threshold) in functional style only: no
for loop, no class. Return the average of the numbers strictly greater
than threshold, rounded to 2 decimals — or 0 if none qualify.
functional_average([10, 25, 30, 45], 20)→33.33functional_average([1, 2], 100)→0
Hint: above = [n for n in nums if n > threshold], then
round(sum(above) / len(above), 2) if above else 0.
Press ▶ Run, try the Live App chips, then ✓ Check. After that, the practice set below moves between all three styles, so the differences stay in your hands, not just in your head.
