Pebellslearn by building
0%
RoadmapsMatrixCoursesSandboxSign in
Functional Python  ›  Lesson

Programming Paradigms

Paradigms 16 minImperative, object-oriented and functional — three styles for the same goal, mixed by what each part of the job needs
Your task
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. Example: functional_average([10, 25, 30, 45], 20) -> 33.33 · functional_average([1, 2], 100) -> 0.

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.

Three columns computing the same average of prices above 20: Imperative is a box with total and count that change lap by lap inside a for-loop; Object-Oriented is an Averager object whose internal state changes each time .add(p) is called; Functional is a filter-then-map-then-sum/len pipeline where nothing already made is ever changed — all three arrows land on one shared "average = 33.33" box

Average price above a threshold, three ways

A café wants the average price of items over ₹20. The same answer, three styles:

python

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:

python

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.

What does this print?
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:

What does this print?
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 total
quick drill

Which paradigm best describes this function's STYLE?

class Counter:
    def __init__(self):
        self.n = 0
    def bump(self):
        self.n += 1
        return self.n
quick drill

Which paradigm best describes this code's STYLE?

What does this print?
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.33
  • functional_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.

Practice — level up

0/5 solved

Solve each in its own editor — Run to try, Check to grade. Stuck? Reveal a Hint, or the full Solution + walkthrough.

Warm-upRewrite a loop in functional style

Write squares_functional(nums) returning the square of every number in nums, using a list comprehension — no for statement building up a result list by hand.

functional-programming
DrillA pure function, on purpose

Write add_tax_pure(price, rate) returning price * (1 + rate) rounded to 2 decimals. It must be PURE: no reading or writing anything outside its own parameters and return value — no globals, no printing, no appending to a list defined elsewhere.

functional-programmingpure-function
BuildState that remembers, on purpose

Write a class RunningTotal with __init__ starting a total at 0, add(x) adding x to it, and value() returning the current total. Then write totals_after(xs) that builds ONE RunningTotal, adds every number in xs in order, and returns its final value().

object-orientedstate
BossThe same average, with filter instead of a comprehension

Write average_above_functional(nums, threshold) returning the average of the numbers strictly greater than threshold, rounded to 2 decimals (or 0 if none qualify) — this time using filter() and a lambda instead of a comprehension.

functional-programmingfilter
CapstoneThe same report, built the OOP way

Write a class PriceLedger with record(price) accumulating a running total and count, and stats() returning {"total": ..., "count": ..., "average": ...} (average rounded to 2 decimals, 0 if nothing was recorded). Then ledger_report(prices) builds ONE PriceLedger, records every price, and returns its final stats().

object-orientedstate
functional_average.py
Call the function you wrote — just like your app's frontend would. Edit the inputs and hit Call.
functional_average(nums, threshold) → floatAverage of the numbers above threshold, computed with a comprehension and built-ins only.
try:
returned
No app map for this section yet.
Press Run to execute your code.
Press Check to run the tests for this lesson.