Pebellslearn by building
0%
RoadmapMatrixCatalogSandboxSign in
Fundamentals  ›  Lesson

Loops

Loops 13 minRepeating work with for and while
You're building a piece ofBill Splitter & Tip Calculator
This piece — sum_items(): Adds up the individual food items on the check.
Scenario The table ordered soup ($10), steak ($20) and a shared dessert ($5.50). The app totals the items before tip.
Your task
Build sum_items(prices). It adds up a list of prices with a loop and returns the total. Example: sum_items([10, 20, 5.5]) → 35.5.

Loops

A loop repeats a block of code. Python has two:

for — do something for each item

prices = [10, 20, 5.5]
for price in prices:
    print(price)      # runs once per item: 10, then 20, then 5.5

for x in <collection> walks through a list (or string, or range) one item at a time, putting each into x. This is the loop you'll reach for 90% of the time.

❓ Cross-question — "Is for x in xs like JS for...in or for...of?" It's for...of — you get the values, not indices. (JS for...in gives keys/indices — that trap doesn't exist here.) Looping a dict directly gives its keys, so for k in menu:for (const k of Object.keys(menu)). Need the index too? Use enumerate (below).

The accumulator pattern

To total a list, keep a running sum. Set it to 0 before the loop, add to it inside the loop, use it after:

total = 0                # 1) start empty
for price in prices:
    total = total + price   # 2) add each item  (or: total += price)
print(total)             # 3) 35.5

Indentation decides what's "inside" the loop. If return total is indented under the for, it runs on the first item and quits early. It must be at the same level as total = 0 — after the loop.

Step through it and watch total climb — this is the pattern behind almost every "add up the bill" function you'll ever write:

step through it
1prices = [10.0, 20.5, 5.0]
2total = 0
3for price in prices:
4 total += price
5print(total)

Here is that early-return mistake as running code. It returns after the first price instead of the whole list:

broken — fix it

This should print 35, the sum of all three prices. It prints 10. Run it, then fix the indentation.

range() — loop a set number of times

for i in range(5):       # 0, 1, 2, 3, 4
    print(i)

for i in range(1, 4):    # 1, 2, 3  (start, stop)
    print(i)

while — repeat until a condition is false

count = 3
while count > 0:
    print(count)
    count -= 1           # MUST change, or it loops forever

The #1 while-loop bug: forgetting to change the condition variable, so it never becomes false → an infinite loop. (Our sandbox will stop it after a few seconds, but avoid it!)

break, continue, and pass

Three little words steer what happens inside a loop:

  • breakstop the whole loop immediately.
  • continueskip to the next item, ignoring the rest of this round.
  • passdo nothing — a placeholder that means "no action here."
python

pass is the odd one out — it does nothing at all. Python sometimes requires a statement (you can't leave the block after a : empty), so pass fills the gap. Use it as a placeholder while you're sketching, or when a branch genuinely has nothing to do:

python

break vs continue vs pass: break leaves the loop, continue jumps to the next round, and pass just falls through to the next line. pass isn't loop-only — it's Python's "empty statement," used anywhere a line is required but you have none (empty functions, if branches, class bodies).

for … else — "we finished without breaking"

A loop can have an else. It's an unusual piece of Python and the name is genuinely misleading, so read the rule carefully: else runs when the loop finished normally — that is, when break never fired.

What does this print?
for code in ["SAVE10", "STUDENT"]:
    if code == "HALF":
        print("found it")
        break
else:
    print("no HALF code")

Read it as "for … then, if nothing broke out". It saves you the "did I find it?" flag variable you'd otherwise have to keep:

python

Idioms & real-world patterns

Basic for/while loops work, but Python rewards going higher-level. These patterns are what fluent Python looks like — shorter, faster, clearer.

enumerate — index and item, together

Need the position while looping? Don't manage a counter by hand:

python

Coming from Java/JS: replaces for (let i = 0; i < a.length; i++) and JS forEach((x, i) => …) — but the order is (i, x), index first.

zip — walk two lists in lockstep

zip pairs items position-by-position, stopping at the shortest:

python

List comprehensions — a loop that builds a list

The single most important Python idiom. It turns "make an empty list, loop, append" into one expression:

python

Add a trailing if to filter:

python

Coming from Java/JS: this replaces .map() / .filter() chains. nums.map(x => x*2).filter(x => x > 3)[x*2 for x in nums if x*2 > 3].

❓ Cross-question — "Can't I just call .map() on a list?" No — Python lists have no .map/.filter/.reduce methods. The comprehension is the idiom (the free functions map()/filter() also exist, but comprehensions read better), and it builds a real list, not a lazy iterator.

Dict & set comprehensions

Same shape, different braces — build a dict with key: value, or a set (auto-unique):

python

⚡ Advanced — generators: build items lazily

A generator expression looks like a list comprehension with () instead of [], but it doesn't build the whole list — it yields items one at a time, on demand. Ideal for huge streams or feeding sum()/any()/max():

python

A generator function uses yield to produce values across calls, pausing and resuming its own state — memory stays flat no matter how many items:

python

Coming from Java/JS: yield is like a JS generator (function*), and a lazy generator is like a Java Stream — nothing is computed until you iterate it.

❓ Cross-question — "When do I use a generator (…) vs a list […]?" Use a list comp […] when you need the whole thing now — indexing, len, reuse (eager, like a JS array). Use a generator (…) for large/streaming data or a one-pass feed into sum()/any()/max() (lazy, single-use, like a JS generator). Iterating a generator a second time yields nothing.

⚡ Advanced — itertools, the loop power tools

The itertools module has battle-tested building blocks. Two you'll reach for:

python

🎯 Your turn

Write sum_items(prices) — it adds up a list of prices with a loop and returns the total:

  • sum_items([10, 20, 5.5])35.5
  • sum_items([])0

Hint — start total = 0, then for price in prices: total += price, and return total after the loop.

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-upDouble every number

Write double_all(nums) returning a new list with every number doubled, using a list comprehension.

comprehension
DrillRunning totals

Write running_totals(nums) returning the list of running sums — [10, 20, 5][10, 30, 35].

loopsaccumulator
BuildInvoice lines

Write invoice_lines(names, prices, qtys) — three parallel lists — returning strings like "soup x2 = $20.00" (price × qty, 2 decimals). Use zip.

zipcomprehensionf-strings
BossPair sums

Write pair_sums(nums, target) returning every pair [a, b] (earlier index first, no repeats) whose values add to target, in order.

nested-loopscomprehensionconditionals
CapstoneSales summary

Write sales_summary(days, sales) (parallel lists) returning a dict {"total", "best_day", "average"} — total sales, the day with the highest sales, and the mean rounded to 2 decimals. Empty input → {"total": 0, "best_day": None, "average": 0}.

zip/indexsum/maxdictf-strings
sum_items.py
Call the function you wrote — just like your app's frontend would. Edit the inputs and hit Call.
sum_items(prices) → floatAdd up every price in the list.
try:
returned
Press Run to execute your code.
Press Check to run the tests for this lesson.
Loops — Pebells