What’s one thing you learned? What’s still confusing?
Dictionaries & Hash Tables: O(1) Lookup Explained
Python dicts: hash mechanics, collision resolution, all dict methods, and comprehensions.
Mini-Project: Word Counter
Build a word-frequency counter — the foundation of every NLP pipeline.
Strings: Methods, f-strings & Text Processing
All string methods, f-string format specs, Unicode/UTF-8, bytes vs str, and regex basics.
Interactive Labs for This Track
Loop Visualizer
You're a factory robot repeating the same task on an assembly line — watch how loops automate repetitive work
List Slicing
You have a playlist of 50 songs — grab just tracks 10 through 20 with a single slice expression
Sorting Algorithms
You're organizing a library of 10,000 books — which sorting method is fastest?
Ask questions, share insights
A small report generator that takes a list of grades for a class and prints: mean, min, max, total students, number passing (>= 60), pass rate (%), and the list of students above 90.
Class Grade Report
==================
Total students: 30
Average grade: 72.4
Highest grade: 98
Lowest grade: 31
Passing (>= 60): 24 (80.0%)
Top scorers (> 90): [92, 95, 98, 91]
sum(), min(), max(), and len() togetherifStart with a list of 10 grades. Hardcoded is fine -- it lets you focus on the stats logic without input handling getting in the way.
grades = [85, 92, 78, 45, 67, 71, 88, 33, 95, 58]
print(grades)This is your dataset. Every step below is a question we'll ask of this same list.
What does `len(grades)` return?
sum, min, max, and len are all built-in -- no imports needed. The mean is sum / len.total = sum(grades)
average = total / len(grades)
highest = max(grades)
lowest = min(grades)
print(f"Average: {average:.1f}")
print(f"Highest: {highest}")
print(f"Lowest: {lowest}"):.1f rounds to one decimal place -- cleaner than printing 71.2999999. Tweak the precision based on the audience (.2f for currency, .0f for whole-number percentages).What does sum([85, 92, 78]) / 3 produce?
/ operator ALWAYS returns a float, even when the math comes out clean: 255 / 3 is 85.0, not 85. If you wanted an integer, you'd use // (floor division). For averages this is exactly what you want — 255 / 4 is 63.75, not the misleading 63 that integer division would give. This is a deliberate Python 3 fix to a Python 2 footgun where 5 / 2 silently became 2.if to filter.passing = [g for g in grades if g >= 60]
print(passing) # [85, 92, 78, 67, 71, 88, 95]
print(len(passing)) # 7 -- the countpassing.append(g).What does this print? grades = [85, 45, 92] passing = [g for g in grades if g >= 60] print(grades)
grades is untouched; the filtered values live in passing. This is a critical pattern: comprehensions are non-destructive. If you wanted to actually remove failing grades from the original list, you'd need to reassign: grades = [g for g in grades if g >= 60]. Beginners often forget this and wonder why their "filter" didn't change anything.What does `[g for g in grades if g >= 60]` build?
len(grades) to get a percentage. Then do another comprehension for top scorers.pass_rate = len(passing) / len(grades) * 100
top = [g for g in grades if g >= 90]
print(f"Passing: {len(passing)} / {len(grades)} ({pass_rate:.1f}%)")
print(f"Top scorers (>= 90): {top}")pass_rate is a float -- :.1f formats it cleanly. This is the same skill you'll use later to print model accuracy: f"Accuracy: {accuracy:.1%}".What does this print? rate = 0.847 print(f"{rate:.1%}")
% format spec is doing TWO things at once: multiplying by 100 AND appending the percent sign. So 0.847 becomes 84.7, then .1 rounds to one decimal place, then % gets added. A common bug: writing f"{rate*100:.1%}" produces 84700.0% because you've multiplied twice. Either use :.1% on the raw fraction, or use :.1f and add % manually — never mix them.grades = [85, 92, 78, 45, 67, 71, 88, 33, 95, 58, 73, 81]
print("Class Grade Report")
print("=" * 18)
print(f"Total students: {len(grades)}")
print(f"Average grade: {sum(grades) / len(grades):.1f}")
print(f"Highest grade: {max(grades)}")
print(f"Lowest grade: {min(grades)}")
passing = [g for g in grades if g >= 60]
top = [g for g in grades if g >= 90]
pass_rate = len(passing) / len(grades) * 100
print(f"Passing (>= 60): {len(passing)} ({pass_rate:.1f}%)")
print(f"Top scorers (>= 90): {top}")Twelve students, one report. Run it -- you have a real piece of data analysis written entirely in standard library Python.
You're given a list of 30 grades. Write a function `report(grades)` that prints the full class report: count, average, min, max, count passing (>= 60), pass rate %, and the list of top scorers (>= 90). Use list comprehensions for the filters and built-in aggregates for everything else.
Class Grade Report
==================
Total students: 30
Average grade: 71.5
Highest grade: 98
Lowest grade: 31
Passing (>= 60): 23 (76.7%)
Top scorers (>= 90): [92, 95, 90, 91, 98]grades = [85, 92, 78, 45, 67, 71, 88, 33, 95, 58,
73, 81, 49, 90, 65, 70, 38, 84, 91, 77,
82, 31, 60, 98, 75, 53, 86, 79, 64, 87]
def report(grades):
# TODO: print "Class Grade Report" with a "=" underline
# TODO: total students, average grade, highest, lowest
# TODO: passing comprehension + pass rate
# TODO: top scorers comprehension
pass
report(grades)
Try one of these variations:
letter_grade(g) that returns 'A' (>=90), 'B' (>=80), 'C' (>=70), 'D' (>=60), 'F' (<60). Print a count for each letter using a dict comprehension.((sum((g - mean) ** 2 for g in grades)) / len(grades)) ** 0.5. Print it alongside the average.*. A row like 90-99: ***** (5 stars = 5 students).