In lesson 8-3, if not cart: was true when cart was an empty list. Empty containers count as false in Python without any explicit length check.
That behavior is called truthiness, and this lesson makes it official alongside the other idioms that separate fluent Python from Python that reads like it was translated out of another language.
Loop like a local
These idioms exist because Python is read far more often than it is written. Teams standardize on them, reviewers expect them, and interviewers quietly grade on them. Each one replaces bookkeeping with stated intent.
Three rewrites cover most unpythonic loops.
When you need the index as well, use enumerate, never range(len(...)):
for i, word in enumerate(words, start=1): print(i, word)
When you are walking two sequences in step, use zip:
for name, score in zip(names, scores): print(name, score)
When you are moving several values at once, unpack:
low, high = high, low # swap, no temp variable first, *rest = [1, 2, 3, 4] # first=1, rest=[2, 3, 4]
And truthiness: an empty string, empty list, empty dict, 0, and None are all falsy, so write if items: rather than if len(items) > 0:. There is one important exception. To tell None apart from an empty container, compare with is None explicitly, exactly as the lesson 2-2 sentinel did.
Four idioms in one short program
enumerate, zip, star unpacking, and a truthiness guard, all in a dozen lines.
names = ["mia", "leo", "zoe"] scores = [82, 91, 78] for rank, (name, score) in enumerate(zip(names, scores), start=1): print(f"{rank}. {name} {score}") best, *others = sorted(scores, reverse=True) print("best:", best, "others:", others) waitlist = [] if not waitlist: print("no one waiting")
Output
1. mia 82 2. leo 91 3. zoe 78 best: 91 others: [82, 78] no one waiting
The for line nests two of the idioms. zip produces (name, score) tuples, enumerate wraps each of those in a (rank, pair) tuple, and the parentheses in rank, (name, score) unpack both levels at once.
best, *others = sorted(...) takes the largest value and keeps the remainder as a list, a neat way to separate a winner from the field in one statement.
Small habits, big readability
- Chained comparisons:
if 0 <= i < len(xs):reads like math and replaces two clauses joined byand. - f-strings for all formatting:
f"{price:.2f}"renders 2 decimal places,f"{count:>5}"right-aligns in 5 columns. - dict.get(key, default) instead of checking
inbefore reading. - EAFP ("easier to ask forgiveness than permission"): Python style prefers trying the operation and catching the specific exception (lesson 8-3) over pre-checking every condition.
None of these change what your program can do. They change how fast the next human, often future you, can read it. That is worth real money in code review.
A numbered price list
Two parallel lists become one numbered report by combining zip with enumerate, and the prices get consistent two-decimal formatting.
products = ["tea", "coffee", "cocoa"] prices = [3.5, 4, 5.25] for i, (name, price) in enumerate(zip(products, prices), start=1): print(f"{i}. {name}: ${price:.2f}")
Output
1. tea: $3.50 2. coffee: $4.00 3. cocoa: $5.25
zip(products, prices) pairs the lists, enumerate(..., start=1) adds human-friendly numbering, and the for line unpacks both levels in one go.
The formatting is what makes the output look professional. {price:.2f} forces exactly two decimal places, which is why 4 prints as $4.00 and 3.5 prints as $3.50. Printing the raw floats would give a ragged column of 3.5, 4, and 5.25.
Swapping and bounds-checking
Two small idioms that show up constantly. Tuple assignment swaps two variables with no temporary, and a chained comparison expresses a range check the way mathematics does.
a, b = 10, 3 a, b = b, a print(a, b) i = 2 xs = [5, 6, 7] if 0 <= i < len(xs): print(xs[i])
Output
3 10 7
The swap works because Python evaluates the entire right-hand side into a tuple before assigning anything, so b, a is captured as (3, 10) while a still holds 10. In languages without this feature the same swap needs a third variable to park a value in.
0 <= i < len(xs) is a chained comparison, and Python evaluates it as 0 <= i and i < len(xs) while only computing i once. It reads like the interval notation from math, which is much easier to scan than the two-condition version.
The right way to loop with indexes
For looping over words with indexes, the Pythonic form is:
for i, w in enumerate(words): print(i, w)
enumerate hands you the index and the item together, so there is no counter to initialize and no chance of the two falling out of step.
The alternative you sometimes see, calling words.index(w) inside the loop to recover the position, is worse than merely ugly. It rescans the list on every iteration, turning an O(n) loop into O(n²), and it returns the first matching position, so it silently reports the wrong index whenever the list contains duplicates.