Pebellslearn by building
0%
RoadmapMatrixCatalogSandboxSign in
Fundamentals  ›  Lesson

Built-in Functions

Builtin Functions 13 minUsing Python's batteries-included toolbox
You're building a piece ofBill Splitter & Tip Calculator
This piece — bill_stats(): Summarises the check: total, average, priciest item.
Scenario The manager wants a quick read on the table: total spend, the average plate, and the priciest item.
Your task
Build bill_stats(prices). It returns a summary dict with keys total, count, average, highest, lowest. Example: bill_stats([10, 20, 30]) → {"total": 60, "count": 3, "average": 20.0, "highest": 30, "lowest": 10}.

Built-in Functions

Python ships with a toolbox of built-in functions — always there, no import needed. For our Bill Splitter, they turn whole loops into single words: total a table with sum, find the priciest plate with max, number a receipt with enumerate. This lesson builds up to bill_stats(prices), the function that gives the manager a one-glance read on any table.

Every box is live — edit and ▶ Run.

Part 1 — Measuring the table: len and sum

Two questions come up on every bill: how many things and how much total. len counts the items; sum adds them all. In lesson 5 you totalled a list by hand with a for loop — sum(prices) does exactly that in one word, faster and clearer.

python

❓ Cross-question — "Why len(x) and not x.length / x.size()?" Python favours free functions over methods for universal operations: len(x), sum(x), min(x), max(x), sorted(x) all work on any sequence (list, tuple, str, set, dict) — one consistent call instead of a different method per type.

Try it: add another plate to prices and re-run. Both numbers update — no loop, no running total, no off-by-one.

Note: sum starts from 0 by default, so sum([]) is 0 (not an error). len([]) is 0 too. These two never crash on an empty list — remember that, it matters in Part 9.

Part 2 — The extremes: min, max, and sorted

max finds the priciest plate, min the cheapest. sorted returns a new list in order and leaves your original untouched — handy when you want a ranked menu but still need the plates in their original serving order.

python

That "classic mix-up" is worth meeting head-on before it costs you an hour:

What does this print?
prices = [24.0, 8.0, 12.5]
cheapest_first = prices.sort()
print(cheapest_first)

Watch out: sorted(prices) gives back a sorted copy. The last line proves prices itself is unchanged. (The list.sort() method sorts in place and returns None — a classic mix-up.) Pass reverse=True to sorted for a most-expensive-first ranking.

❓ Cross-question — "Is sorted(x) like [...x].sort()?" Yes — sorted(x) returns a new list (copy-then-sort), while x.sort() mutates in place and returns None — so never write y = x.sort(). Python's sort is stable, same as JavaScript's.

Part 3 — Money math: abs and round

abs strips the sign — perfect for "how far off" a split is from even. round trims a float to a set number of decimals, which is how you keep dollars looking like dollars instead of 19.8760000001.

python

Watch out — banker's rounding. round(2.5) is 2, not 3, and round(3.5) is 4. Python rounds a tie to the nearest even number. It's deliberate (it reduces bias over many roundings), but it surprises everyone once. For real invoicing use the decimal module; for this playground, round(x, 2) on totals is fine.

Part 4 — enumerate: index and item together

Need the position and the value while looping? enumerate hands you both, so you never have to babysit a manual counter. It yields (index, item) pairs, and start=1 makes a human-friendly numbered receipt.

python

In a real loop you unpack the pair directly (this is the idiom you'll reach for):

for i, name in enumerate(items, start=1):
    print(f"{i}. {name}")

Try it: change start=1 to start=0 (the default) and watch the numbers shift. Menus read better starting at 1; array logic usually wants 0.

Part 5 — zip: two lists in lockstep

Menu names live in one list, their prices in another. zip walks them together, pairing items[0] with prices[0], and so on — no index juggling.

python

zip stops at the shortest list, so mismatched lengths silently drop the extras — a subtle source of "where did my last item go?" bugs:

python

Note: zip is a common partner for sum. To total item-by-item you can loop for name, price in zip(items, prices): and add up as you go.

Part 6 — any and all: quick sanity checks

all is True only when every condition holds; any is True when at least one does. Paired with a generator expression, they validate a whole list in one readable line — "are all prices positive?", "is any plate over $20?".

python

Watch out: on an empty list all([]) is True (nothing broke the rule) and any([]) is False (nothing matched). These "vacuous truth" cases are logically correct but easy to forget when your input might be empty.

Part 7 — range: counting on demand

range produces a sequence of numbers without building a giant list in memory — ideal for "do this N times" or generating table numbers. range(stop) starts at 0; range(start, stop) and range(start, stop, step) give you full control. The stop value is excluded.

python

Note: range is lazy — it doesn't materialise the numbers until you ask. That last line sums 1..100 without ever building the list, which is why sum(range(...)) scales to huge counts effortlessly. Wrap it in list(...) only when you actually want to see the values, as above.

Part 8 — map and filter: transform and select

map applies a function to every item; filter keeps only the items that pass a test. Both are lazy — they return an iterator, so you wrap them in list(...) to see the results. Here we add an 18% tip to each plate, then keep only the higher-priced dishes.

python

Try it: most Pythonistas write these as comprehensions instead — [round(p * 1.18, 2) for p in prices] and [p for p in prices if p >= 20] read more naturally. Know map/filter when you see them, but reach for comprehensions in your own code.

Part 9 — Guarding empty sequences

Here's the trap that shapes today's task: sum([]) and len([]) are happy (they return 0), but max([]) and min([]) raise ValueError. If a table could have zero items, you must check before calling them.

python

Without a guard, max([]) blows up:

max([])   # ValueError: max() iterable argument is empty

Two safe patterns: pass default=0 (shown above), or guard with if not prices: and return early. The graded bill_stats uses the guard — it's explicit and it lets you return a full all-zeros dict in one place.

Watch out: an empty list is falsy, so if not prices: reads as "if there are no prices". This one check is the difference between a clean $0.00 summary and a crash mid-service.

Part 10 — Putting it together: a stats dict

A dictionary maps keys to values (you'll go deep on these in Section 2). It's the perfect return type when a function computes several named results at once — exactly what the manager wants: total, count, average, highest, lowest.

python

Note: every built-in you met above is doing one line of work here. Now add the Part 9 empty-list guard on top and you have bill_stats — the function you're about to write.


Part 11 — Sorting & selecting with a key

You've met sorted, min, max, map. Their real power is the key= argument — a function that says what to compare by. This is the single most useful builtin idiom for real data.

sorted(key=…) — sort by anything

python

Coming from Java/JS: key= is a selector (return the field to sort on), not a two-arg comparator. It boils JS's arr.sort((a,b) => a.price - b.price) down to key=lambda d: d.price — and each key is computed once.

❓ Cross-question — "How do I do a multi-field comparator like in TS?" Return a tuple from key — Python compares tuples left to right, so key=lambda x: (a, b) sorts by a then breaks ties by b. For descending on a number, negate it (-x.score). No chained .thenComparing() needed.

min/max also take key=

python

Multi-key sort — sort by A, break ties by B

Return a tuple from the key; Python compares tuples element by element:

python

operator.itemgetter(1) does the same as lambda d: d[1], faster and clearer:

python

reversed() and map over two lists

python

Real-world — a leaderboard: sort players by score (high→low), then name:

python

🎯 Your turn

Write bill_stats(prices) — it returns a summary dict with keys total, count, average, highest, lowest:

  • bill_stats([10, 20, 30]){"total": 60, "count": 3, "average": 20.0, "highest": 30, "lowest": 10}
  • bill_stats([]){"total": 0, "count": 0, "average": 0.0, "highest": 0, "lowest": 0}

Hint — guard the empty list first, then use sum, len, max, min and round.

Then press ▶ Run, tap the Live App try chips to call it with different inputs, and hit ✓ Check. Green = this piece of the app is built. ✅

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-upAverage

Write average(nums) returning the mean rounded to 2 decimals, or 0 for an empty list.

sumlenguard
DrillTop three

Write top3(nums) returning the three largest values, highest first. Fewer than three? return them all sorted high→low.

sortedslice
BuildSort dishes by price

Each item is [name, price]. Write sort_by_price(items) returning just the names, ordered cheapest → priciest.

sorted-keycomprehension
BossLeaderboard

Each player is [name, score]. Write leaderboard(players) returning ranked strings "<rank>. <name> — <score>", sorted by score descending, ties broken by name ascending, ranks starting at 1.

multi-key-sortenumeratef-strings
CapstoneRecord stats

Each record is [name, value]. Write stats(records) returning {"count", "total", "average", "highest", "lowest"} where highest/lowest are the whole [name, value] with the max/min value. Empty → count/total/average 0 and highest/lowest None.

min/max-keysumguarddict
bill_stats.py
Call the function you wrote — just like your app's frontend would. Edit the inputs and hit Call.
bill_stats(prices) → dictSummary stats for a list of prices.
try:
returned
Press Run to execute your code.
Press Check to run the tests for this lesson.