Lists, dicts & comprehensions in depth
You met lists and dictionaries in the basics lesson. Real programs spend most of their time moving data between these structures, so knowing the idioms well makes your code shorter, faster and easier to read.
Slicing and copying lists
A slice has the form items[start:stop:step]. The start is included, the stop is excluded, and any part can be skipped.
months = ["Jan", "Feb", "Mar", "Apr", "May", "Jun"]
print(months[:3]) # ['Jan', 'Feb', 'Mar'] - first quarter
print(months[::2]) # ['Jan', 'Mar', 'May'] - every second month
print(months[::-1]) # reversed copy
backup = months[:] # shallow copy; months.copy() does the same
Sorting with a key
sorted() returns a new list, while list.sort() sorts in place and returns None. The key argument decides what to compare.
orders = [
{"id": 101, "city": "Surat", "amount": 4200},
{"id": 102, "city": "Indore", "amount": 950},
{"id": 103, "city": "Kochi", "amount": 7800},
]
top = sorted(orders, key=lambda o: o["amount"], reverse=True)
print([o["id"] for o in top]) # [103, 101, 102]
Dictionary techniques
| Task | Idiom |
|---|---|
| Read with a fallback | d.get("key", 0) |
| Loop over pairs | for k, v in d.items(): |
| Merge two dicts | merged = a \| b (Python 3.9+) |
| Count things | collections.Counter(items) |
| Group into lists | collections.defaultdict(list) |
from collections import Counter, defaultdict
sales = [("Surat", 4200), ("Indore", 950), ("Surat", 1300), ("Kochi", 7800)]
by_city = defaultdict(list)
for city, amount in sales:
by_city[city].append(amount)
totals = {city: sum(vals) for city, vals in by_city.items()}
print(totals) # {'Surat': 5500, 'Indore': 950, 'Kochi': 7800}
print(Counter(c for c, _ in sales).most_common(1)) # [('Surat', 2)]
Comprehensions: list, dict, set and nested
Comprehensions read left to right: expression, then loop, then an optional filter.
prices = {"saree": 2400, "kurta": 899, "dupatta": 450}
discounted = {item: round(p * 0.9) for item, p in prices.items() if p > 500}
initials = {name[0] for name in prices} # set comprehension
grid = [(r, c) for r in range(2) for c in range(3)] # nested loops, outer first
Use zip() to walk two lists together and enumerate() when you need the position:
names = ["Asha", "Ravi"]
scores = [88, 74]
for rank, (name, score) in enumerate(zip(names, scores), start=1):
print(rank, name, score)
Common mistakes
- Using a mutable default argument such as
def add(x, items=[])— the same list is shared across calls. UseNoneand create the list inside. - Writing
new = oldand expecting a copy. Both names point to the same list. - Building huge comprehensions with three levels of nesting. If it needs a comment to explain, use a normal loop.
- Forgetting that
list.sort()returnsNone, sox = x.sort()wipes your data.
Practice
Given a list of (product, category, price) tuples for a textile shop, build a dict mapping each category to the
name of its most expensive product, and a second dict with the average price per category rounded to 2 decimals.