Python Basicsยถ
Revised August 2026 โ Reorganized for CSP-First-Semester / PCEP-Second-Semester
Python is an open-source, high-level programming language that is widely considered one of the best first languages to learn. It supports multiple programming paradigms โ including procedural, functional, and object-oriented programming (OOP) โ and is backed by an enormous standard library and community. As of 2024, it is one of the most in-demand languages in the job market, used in web development, data science, artificial intelligence, IoT, and cybersecurity.
This course is designed to prepare you for:
๐ฏ AP Computer Science Principles (AP CSP) โ College Board performance task and exam
๐ PCEPโข โ Certified Entry-Level Python Programmer (Exam PCEP-30-02, OpenEDG Python Institute)
How to use this document โ READ THIS FIRST: Topics are now organized around when you need them for the AP CSP Create Performance Task (CPT), not around the PCEP exam blueprint. Everything you need to write, test, and explain your CPT program lives in the main body of this document (Sections 1โ4, File Operations, Data Structures, OOP). PCEP-only exam content that isnโt required for the CPT โ numeral systems, bitwise operators, built-in functions like
map/filter, lambda functions, frozensets, and the full exception hierarchy tree โ has been moved to PCEP Certification Path โ Semester 2 at the end of this document, where youโll study it at a slower pace after the CPT is done, over the second half of the year. PCEP exam objectives are labeled๐ PCEPand AP CSP standards are labeled๐ AP CSP. Activities and projects are marked with###. New this revision: ๐ค Thinking Question checkpoints after every new concept, a ๐ Worked Example before every assignment, and ๐งฉ Scaffolding checkpoints for multi-day projects.
๐ Python Pacing Guide โ Semester 1 (AP CSP Focus)ยถ
Aug 17 โ Dec 18, 2026
Week |
Dates |
Content Focus |
Notebook Spread |
Activities / Projects |
PT / CPT In-Class Time |
Weekly Min |
|---|---|---|---|---|---|---|
1 |
Aug 17โ21 |
How Python Works; Style; Debugging; Variables & Data Types |
Spreads 1 & 2 |
โ |
โ |
216 |
2 |
Aug 24โ28 |
Variables (cont.); Operators |
Spread 3 |
Movie Ticket Eligibility Checker |
โ |
216 |
3 |
Aug 31โSep 4 |
Print Statements & Input/Output |
Spread 4 |
Hello, World! Variations |
โ |
216 |
4 |
Sep 7โ11 |
Conditional Statements |
Spread 5 |
Grade Calculator |
โ |
216 |
5 |
Sep 14โ18 |
Loops |
Spread 6 |
Nested Loop Pattern Design Studio |
โ |
216 |
6 |
Sep 21โ25 |
Strings |
Spread 7 |
Receipt Formatter |
โ |
216 |
7 |
Sep 28โOct 2 |
Lists |
Spread 8 |
โ |
โ |
216 |
8 |
Oct 5โ9 |
Tuples & Dictionaries; Practice PT 1 Introduced |
Spread 9 |
Student Contact Book |
40 min (Fri) Practice |
216 |
9 |
Oct 12โ16 |
Functions; Practice PT 1 in-class work |
Spread 10 |
Math Operations Calculator; Recursion Practice โ Three Levels |
176 min (2ร88) Practice |
216 |
10 |
Oct 19โ23 |
Functions (cont.); Modules and Packages; Practice PT 1 in-class work |
โ |
Project: Random Trivia Quiz Generator |
176 min (2ร88) Practice |
216 |
11 |
Oct 26โ30 |
Exception Handling; Practice PT 1 Due (Fri) |
Spread 11 |
Safe Calculator |
88 min (1ร88) Practice |
216 |
12 |
Nov 2โ6 |
File Operations; Data Structures (Sets, Stacks/Queues); Practice PT 2 Introduced |
Spread 12 |
Student Roster File Manager |
176 min (2ร88) Practice |
216 |
13 |
Nov 9โ13 |
OOP |
Spread 13 |
OOP Zoo (3 options) |
128 min (88+40) Practice |
216 |
14 |
Nov 16โ20 |
CPT Checklist Review & Practice PT 2 Workshop (Mon/Wed); Practice PT 2 Due (Fri) |
โ |
โ |
40 min (Fri) Practice |
216 |
15 |
Nov 23โ27 |
THANKSGIVING โ No School |
โ |
โ |
โ |
โ |
16 |
Nov 30โDec 4 |
AP CSP CPT โ Brainstorming Workshop |
Spread 15 |
CPT Brainstorming: students develop an original program idea |
216 min Official CPT |
216 |
17 |
Dec 7โ11 |
AP CSP CPT โ Writing Code, Recording Video, Developing PPR Responses |
โ |
Official CPT Program (original idea, in progress) |
216 min Official CPT |
216 |
18 |
Dec 14โ18 |
AP CSP CPT โ Final Coding/Submission; CPT Due (Fri, Dec 18); End-of-Course Reflection |
Spread 16 |
Official CPT Program (original idea, due) |
216 min Official CPT |
216 |
Practice PT 1 & 2 are formative โ students write a full program to an actual College Boardโstyle CPT prompt so theyโve rehearsed the task before the real one, but this time is not counted toward the AP CPTโs official minimum. Practice PT 1 total โ 480 min (Weeks 8โ11). Practice PT 2 total โ 344 min (Weeks 12โ14). See AP CSP Performance Task Preparation below for idea lists for both.
Per AP College Board CPT requirements, the official CPT must be an original student idea. Students may not submit Practice PT 1, Practice PT 2, or any other program theyโve already completed as their CPT โ the program developed during Weeks 16โ18 must be new. The Final Project Options (A/B/C) may still be offered as optional starting frameworks/scaffolds, but each studentโs actual CPT submission โ the idea, the implementation, and the individual creative work behind it โ must be their own original work developed within the official CPT window.
Official AP CSP CPT (the actual, submitted Create Performance Task) happens entirely in December: Week 16 = brainstorming/planning an original idea, Week 17 = coding + video + PPR written responses, Week 18 = final coding, submission (due Fri, Dec 18), and course wrap-up โ all three are full 5-day weeks. Total Official CPT Time: 216 + 216 + 216 = 648 min (โ10.8 hrs) โ clears the 9-hour College Board minimum with room to spare, entirely within December, even after setting aside time on the last day for End-of-Course Reflection.
โ ๏ธ What changed from the previous version: The old Week 14 PCEP exam-prep cram day is gone. PCEP-specific exam content (numeral systems, bitwise operators, built-in functions, lambdas, frozensets, the full exception hierarchy) has been pulled out of Semester 1 entirely โ see the note above the pacing table. That gives Weeks 1โ14 room to slow down, add more worked examples, and add the CPT-checklist workshop in Week 14 instead of a PCEP cram session. PCEP prep now happens at a sustainable pace across Semester 2 (JanโJune) in PCEP Certification Path โ Semester 2, after the CPT is behind you.
Python Style Guidelinesยถ
๐ Click to expand PEP 8 Guidelines
PEP 8 Guidelines is the official style guide for Python code. Following these standards is required on all assignments and is tested on the PCEP exam.
๐ PCEP 1.2 โ Pythonโs logic and structure; indentation; PEP-8 recommendations
๐ PCEP 1.3 โ Naming conventions; implementing PEP-8
All Projects Must Include a Header Block:
# Assignment: Program [number]: [Assignment Title]
#
# Author: [Your Name]
# Partner: [Partner's Name]
#
# Course Name: [Course Name]
# Instructor: [Instructor Name]
# Due Date: [Due Date and Time]
#
# Description: [Describe the program's goal, IN DETAIL.]
#
# Language: Python 3.x
# Ex. Packages: [List any external packages used]
#
# Deficiencies: [Known problems, or state there are none.]
1. Use Descriptive Variable and Function Names
Guideline: Use
snake_casenames that clearly describe their purpose.Example:
# Good total_cost = price * quantity # Bad x = p * q
2. Use Consistent Indentation (4 Spaces)
Guideline: Use four spaces per indentation level; do not use tabs.
Example:
def calculate_area(radius): return 3.14 * radius ** 2
3. Limit Line Length to 79 Characters
Improves readability on all devices and screen sizes.
4. Use Blank Lines to Separate Code Sections
Two blank lines between top-level functions/classes; one blank line between class methods.
5. Use Docstrings to Document Functions and Classes
Example:
def calculate_area(radius): """Calculate the area of a circle given its radius.""" return 3.14 * radius ** 2
6. Use Spaces Around Operators
result = (a + b) * (c - d) # Good
result=(a+b)*(c-d) # Bad
7. Avoid Excessive Nesting โ break complex logic into smaller functions.
8. Use List Comprehensions for Simple Operations
squares = [x ** 2 for x in range(10)] # Good
9. Handle Exceptions Properly โ use specific exception types, never bare except.
10. Use Meaningful Constants Instead of Magic Numbers
TAX_RATE = 0.15
total_cost = price * (1 + TAX_RATE)
11. Avoid Global Variables โ use function parameters or class attributes.
12. Use is for Comparison to None
if value is None: # Good
if value == None: # Bad
13. Organize Imports Properly โ standard library โ third-party โ local modules.
14. Use Type Annotations (Python 3.5+)
def calculate_total(cost: float, tax_rate: float) -> float:
return cost * (1 + tax_rate)
Debugging Strategiesยถ
๐ AP CSP: CRD-2.J โ Identify and correct errors in algorithms and programs.
Types of Errors
Error Type |
Description |
Example |
|---|---|---|
Syntax Error |
Code violates Python grammar rules |
Missing |
Runtime Error |
Code crashes while running |
Dividing by zero |
Logic Error |
Code runs but gives wrong output |
Using |
Debugging Strategies
Read the Error Message Carefully โ find the exact line number, identify the error type.
Trace the Program Step-by-Step โ walk through code line by line; track variable values.
Use Print Statements โ insert
print("Value of score:", score)to expose variable state.Test with Simple Inputs โ start with values where you know the expected output.
Isolate the Problem โ comment out sections; test smaller pieces individually.
Check Variables and Data Types โ confirm types with
type().Review Logic and Conditions โ check
if, loop conditions, and operator use carefully.Rubber Duck Debugging โ explain the code out loud step-by-step; mistakes often surface.
Use Incremental Development โ write and test small pieces before adding features.
Take a Break and Revisit โ fresh eyes catch simple mistakes.
Ask for Help with Evidence โ be ready to explain: what it should do, what it does, what you tried.
Student Debugging Checklistยถ
Before asking for help, confirm:
โ Read the error message carefully
โ Checked the exact line number and nearby code
โ Added print/debug statements to trace variables
โ Tested with simple, predictable inputs
โ Traced variable values step-by-step
โ Explained the code out loud (Rubber Duck Method)
Section 1 โ Computer Programming and Python Fundamentalsยถ
๐ AP CSP: CRD-2.A, CRD-2.B
How Python Worksยถ
๐ PCEP 1.1 โ Understand fundamental terms and definitions
Python is an interpreted language. Instead of being compiled into machine code before running, a Python interpreter reads and executes your source code line-by-line at runtime.
Concept |
Definition |
|---|---|
Source code |
The human-readable Python instructions you write |
Interpreter |
The program that reads and runs your Python code |
Compiler |
Translates entire source code to machine code before running (e.g., C, Java) |
Lexis |
The vocabulary of the language โ valid words/tokens Python recognizes |
Syntax |
The grammar rules for how code must be structured |
Semantics |
The meaning of correctly written code |
A SyntaxError means Python cannot understand your code structure. A logic error means Python understands it but does something you didnโt intend.
๐ค Thinking Question โ check your answer
Question: Your program runs without crashing, but it prints the wrong total every time. Is this most likely a syntax error, a runtime error, or a logic error โ and how do you know?
Answer: A logic error. Python ran the code successfully (no crash, no error message), so the syntax was fine and nothing โbrokeโ at runtime โ the program just did something other than what you intended. Youโd fix this by tracing your math/logic step by step, not by looking for a typo.
Python Keywords and Structureยถ
๐ PCEP 1.2 โ Understand Pythonโs logic and structure
Keywords are reserved words that Python uses for specific purposes. You cannot use them as variable names. This is a reference table youโll come back to all year โ bookmark it.
False None True and as assert
async await break class continue def
del elif else except finally for
from global if import in is
lambda nonlocal not or pass raise
return try while with yield
What each keyword does (quick-reference glossary):
Keyword |
Purpose |
|---|---|
|
The two Boolean values |
|
Represents โno valueโ |
|
Logical operators for combining/reversing conditions |
|
Conditional branching |
|
Loop constructs |
|
Loop control โ exit early, skip an iteration, or do nothing |
|
Defines a function |
|
Sends a value back from a function |
|
Defines a class (blueprint for objects) |
|
Bring in code from modules |
|
Exception handling |
|
Membership test ( |
|
Identity comparison (same object in memory, not just equal value) |
|
Change which variable scope a name refers to |
|
Defines a small anonymous function (PCEP deep dive โ see Semester 2) |
|
Deletes a variable, list item, or dictionary key |
|
Opens a resource (like a file) and guarantees itโs closed afterward |
|
Checks that a condition is true; raises an error if not (used in testing) |
|
Advanced generator/async syntax โ not used in this course |
Indentation is not optional in Python โ it defines code blocks. Incorrect indentation causes a IndentationError.
# Correct indentation
if True:
print("Indented correctly") # 4 spaces
# Wrong
if True:
print("This will crash") # IndentationError
Comments are notes for humans; Python ignores them:
# This is a single-line comment
A docstring is a special string placed as the very first line inside a function, class, or module to document what it does. Unlike a # comment, Python actually stores a docstring as part of the object โ it can be viewed later with help() or .__doc__.
def area_of_rectangle(length, width):
"""Calculate and return the area of a rectangle."""
return length * width
print(area_of_rectangle.__doc__) # Calculate and return the area of a rectangle.
|
|
|
|---|---|---|
Purpose |
Note for a human reading the code |
Documentation for someone using the function |
Location |
Anywhere |
Must be the first line inside a function/class/module |
Stored by Python? |
No โ ignored by the interpreter |
Yes โ accessible via |
Rule of thumb: a comment explains a line to a fellow programmer; a docstring is the instruction manual for someone who will never open the functionโs source code.
๐ค Thinking Question โ check your answer
Question: You write if True print("hi") and Python refuses to run it before ever touching the print statement. Whatโs missing, and what category of error is this?
Answer: The colon : after the condition is missing โ every if, for, while, def, and class header needs one. This is a syntax error: Python canโt even understand the structure of the line, so it never gets far enough to try running it.
Variables and Data Typesยถ
๐ PCEP 1.3 โ Introduce literals and variables
๐ AP CSP: DAT-1.A โ Explain how data can be represented using bits.
A variable is a named location in memory that stores a value. Python uses dynamic typing โ the data type is inferred from the assigned value, not declared in advance.
age = 25 # int
name = "Alice" # str
price = 9.99 # float
is_student = True # bool
result = None # NoneType
Core Data Types
Type |
Keyword |
Example |
PCEP Focus |
|---|---|---|---|
Integer |
|
|
โ |
Float |
|
|
โ |
String |
|
|
โ |
Boolean |
|
|
โ |
None |
|
|
โ |
List |
|
|
โ |
Tuple |
|
|
โ |
Dictionary |
|
|
โ |
Scientific Notation
Python lets you write very large or very small numbers using e notation, where e means โtimes 10 to the power of.โ This is the same idea as scientific notation in math class (1.5 ร 10โถ), just written on one line without exponents or superscripts.
1.5e6means1.5 ร 10^6โ move the decimal point 6 places right โ1,500,000.02.5e-4means2.5 ร 10^-4โ move the decimal point 4 places left โ0.00025
big_num = 1.5e6 # 1,500,000.0
tiny_num = 2.5e-4 # 0.00025
Both
big_numandtiny_numare stored asfloattype โenotation is just a display/input shortcut, not a separate data type.
Type Casting โ converting between data types:
x = int("42") # str โ int
y = float(7) # int โ float
z = str(3.14) # float โ str
b = bool(0) # int โ bool (0 = False, anything else = True)
Floating-point accuracy:
0.1 + 0.2does not equal exactly0.3in Python due to how floats are stored in binary. This is a known limitation tested on the PCEP exam.
print(0.1 + 0.2) # 0.30000000000000004
print(round(0.1 + 0.2, 2)) # 0.3
๐ PCEP note: binary/octal/hex numeral systems (writing integers in different bases) are a PCEP-only topic and are covered in depth in PCEP Certification Path โ Semester 2. You donโt need them for the CPT.
๐ค Thinking Question โ check your answer
Question: age = "25" then later total = age + 5. What happens, and why?
Answer: Python raises a TypeError, because age is a string "25", not an integer โ Python wonโt automatically add a string and an int together. Youโd need total = int(age) + 5 to cast the string to an int first. This exact bug is extremely common with input(), since input() always returns a string.
Operatorsยถ
๐ PCEP 1.4 โ Choose operators and data types adequate to the problem
๐ AP CSP: AAP-2.F โ Use mathematical operations in algorithms.
Arithmetic Operators
Operator |
Meaning |
Example |
Result |
|---|---|---|---|
|
Exponentiation |
|
|
|
Multiplication |
|
|
|
Division (always float) |
|
|
|
Floor division |
|
|
|
Modulo (remainder) |
|
|
|
Addition |
|
|
|
Subtraction |
|
|
String Operators
"Hello" + " World" # Concatenation โ "Hello World"
"Ha" * 3 # Repetition โ "HaHaHa"
Assignment and Shortcut Operators
x = 10
x += 5 # x = x + 5 โ 15
x -= 3 # x = x - 3 โ 12
x *= 2 # x = x * 2 โ 24
x //= 4 # x = x // 4 โ 6
x **= 2 # x = x ** 2 โ 36
Operator Precedence (Highest โ Lowest)
Level |
Category |
Operators |
|---|---|---|
7 (high) |
Exponent |
|
6 |
Multiplication |
|
5 |
Addition |
|
4 |
Relational |
|
3 |
Logical |
|
2 |
Logical |
|
1 (low) |
Logical |
|
Parentheses
()always override precedence โ use them to make code intentions clear.
Relational Operators โ return True or False
Operator |
Meaning |
Example |
|---|---|---|
|
Equal to |
|
|
Not equal |
|
|
Greater than |
|
|
Less than |
|
|
Greater or equal |
|
|
Less or equal |
|
== vs. is
== and is are not interchangeable, even though both can return True/False in a comparison.
Operator |
Checks |
Question it answers |
|---|---|---|
|
Equality of value |
โDo these contain the same data?โ |
|
Identity of object |
โAre these the exact same object in memory?โ |
a = [1, 2]
b = [1, 2]
c = a
print(a == b) # True โ same values
print(a is b) # False โ two different list objects
print(a is c) # True โ c points to the same object as a
Rule of thumb: use
is/is notonly for identity checks โ most importantlyx is None(the Python standard, preferred overx == None). Use==for comparing values (numbers, strings, list contents, etc.).
Boolean / Logical Operators
True and False # False โ both must be True
True or False # True โ at least one must be True
not True # False โ reverses the boolean
Compound Boolean Expressions (expand)
age = 20
has_license = True
# AND: both must be True
is_eligible = age >= 18 and has_license # True
# OR: at least one must be True
can_enter = age >= 18 or has_vip_pass # True
# NOT: reverses the boolean
is_minor = not (age >= 18) # False
# Complex example
gets_discount = (age > 25 or is_eligible) and not has_discount_coupon
๐ค Thinking Question โ check your answer
Question: age = 17 and has_ticket = False. Evaluate age >= 18 or has_ticket and age >= 18 and has_ticket by hand before running any code. Are they the same?
Answer: No. or only needs one side to be True; here both sides are False (17 >= 18 is False, has_ticket is False), so or evaluates to False. and needs both sides True, so itโs also False here โ but thatโs a coincidence of these particular values. Try age = 20: or โ True (first side is true), and โ False (second side is still false). The two operators behave very differently once one side is True.
๐ Worked Example โ compound booleans in a different context (game achievement unlock)
player_level = int(input("Enter player level: ")) completed_tutorial = input("Completed the tutorial? (yes/no): ") difficulty = input("Difficulty (easy/normal/hard): ") # Unlocked if the player is high level, OR a beginner who finished the tutorial on easy is_veteran = player_level >= 10 is_ready_beginner = completed_tutorial == "yes" and difficulty == "easy" achievement_unlocked = is_veteran or is_ready_beginner if achievement_unlocked: print("Achievement unlocked!") else: print("Keep playing to unlock this achievement.")Notice the strategy: break a complex rule into smaller boolean variables (
is_veteran,is_ready_beginner) before combining them withor. That โsmaller pieces firstโ approach is what youโll use to build the Movie Ticket assignmentโs own โ different โ discount rule below.
Activity: Movie Ticket Eligibility Checkerยถ
Write a program that determines whether a person can buy a discounted movie ticket, using compound boolean expressions (and, or, not).
๐งฉ Scaffolding โ build it in this order:
First, just get
ageandhas_idfrom the user andprint()them back to confirm your input works.Write and test the
is_child_or_seniorcondition alone โ print it and check against a few ages by hand.Write and test the
is_discounted_studentcondition alone the same way.Combine them into
gets_discountand print the correct message.Only then add the second theater/adult-present check from Requirement 4 below.
Requirements:
Ask the user for their age and whether they have a student ID (
yes/no).A ticket is discounted if the person is under 13, OR over 65, OR (between 13โ25 AND has a student ID).
Store the result of that full condition in a single boolean variable, e.g.
gets_discount = ..., then print"Discount applies!"or"Full price."based on its value.Add a second check: the theater is closed to anyone under 6 with no adult present โ ask a follow-up question and use
notto write the condition for โan adult is NOT present.โPrint a trace of your boolean logic as a comment above each condition, explaining what it evaluates in plain English (this mirrors how youโll need to explain conditions in your AP CSP written responses).
Starter code:
# Assignment: Program 1: Movie Ticket Eligibility Checker
# Author: [Your Name]
# Course Name: AP Computer Science Principles
# Description: Determines movie ticket discount eligibility using
# compound boolean expressions.
# Language: Python 3.x
# TODO 1) Get age (as int) and has_id ("yes"/"no") from the user
# TODO 2) Build is_child_or_senior
# TODO 3) Build is_discounted_student
# TODO 4) Combine into gets_discount and print the result
# TODO 5) Add the under-6/no-adult-present check
Submit your .py file and test cases showing that your program worked as intended.
PCEP: 1.4 | AP CSP: AAP-2.F
Assignment โ Print Statements and Input/Outputยถ
๐ AP CSP: CRD-2.B โ Implement algorithms in a programming language.
What is an f-string?
An f-string (โformatted string literalโ) lets you embed variables and expressions directly inside a string, instead of gluing pieces together with + or placeholders. You create one by putting the letter f right before the opening quote:
name = "Maya"
age = 15
print(f"{name} is {age} years old.")
# Output: Maya is 15 years old.
Anything inside curly braces { } gets evaluated as Python code, and the result is inserted into the string as text โ even if it wasnโt a string to begin with. Thatโs why age (an int) doesnโt need str() conversion here โ the f-string handles that automatically.
Why โfโ?
The f tells Python โthis string has live code inside it.โ Without the f, curly braces are just literal characters:
print("{name} is {age} years old.")
# Output: {name} is {age} years old. โ not what you want!
print(f"{name} is {age} years old.")
# Output: Maya is 15 years old. โ the f makes the magic happen
You can put more than just variables inside { }
Since the braces run real Python code, you can do math, call functions, or even index into things:
print(f"Next year you'll be {age + 1}.")
# Output: Next year you'll be 16.
print(f"Your name in caps: {name.upper()}")
# Output: Your name in caps: MAYA
Formatting numbers with :
A colon inside the braces lets you control how the value looks โ decimal places, padding, commas, etc.
pi = 3.14159
print(f"Pi rounded: {pi:.2f}")
# Output: Pi rounded: 3.14
big_number = 1000000
print(f"With commas: {big_number:,}")
# Output: With commas: 1,000,000
This is one of the biggest advantages f-strings have over .format() and concatenation.
Comparing all three methods side by side
name = "Maya"
age = 15
# f-string
print(f"{name} is {age} years old.")
# .format()
print("{} is {} years old.".format(name, age))
# concatenation
print(name + " is " + str(age) + " years old.")
All three print the same thing, but notice:
f-string โ variables live right where theyโre used, so itโs the easiest to read, and no manual type conversion is needed.
.format()โ variables are listed separately at the end, so for a long sentence you have to count placeholders to match them up.concatenation โ works, but gets messy fast, and you must remember
str()for anything that isnโt already a string (a common bug source โ forgetting it raises aTypeError).
NOTE: f-strings became the preferred style starting in Python 3.6.
๐ค Thinking Question โ check your answer
Question: Why does print(name + " is " + age + " years old.") crash if age is an int, but print(f"{name} is {age} years old.") works fine with the exact same age variable?
Answer: The + operator for strings requires both sides to already be strings โ Python wonโt silently convert an int for you with +, so it raises a TypeError. An f-stringโs { } braces run an implicit conversion to text for whatever value lands inside them, so age doesnโt need to be pre-converted. This is one of the biggest practical advantages of f-strings.
Overview
Python gives you several ways to build and display output. In this assignment
youโll practice four of them โ f-strings, the format() method, string
concatenation, and the sep=/end= keyword parameters โ by completing the
TODOs embedded in the starter code below. This time, name and age come
from the user instead of being hardcoded, so youโll also practice reading
input from the console.
NOTE: User Input
name = input("Enter your name: ") # always returns a string
age = int(input("Enter your age: ")) # convert to int
price = float(input("Enter price: ")) # convert to float
sep = โ controls what goes between multiple arguments
By default, print() joins multiple comma-separated arguments with a single space. sep= lets you override that separator.
print("A", "B", "C") # A B C (default **`sep=`**" ")
print("A", "B", "C", sep="-") # A-B-C
print("A", "B", "C", sep="") # ABC
print(2026, 8, 20, sep="/") # 2026/8/20
Each comma-separated item is a separate argument โ sep only affects the space between them, not anything else about the string.
end= โ controls what goes after the whole print call
By default, print() ends every call with a newline (โ\nโ), which is why each print() normally starts a new line. end= replaces that trailing newline with whatever string you give it.
print("Hello")
print("World")
Sample Output:
Hello
World
print("Hello", end="! ")
print("World")
Sample Output:
Hello! World
Here, the first print() doesnโt move to a new line โ it ends with โ! โ instead โ so the second print() continues right where the first left off.
Together:
print("A", "B", "C", sep="-", end=" | ")
print("D", "E", "F", sep="-")
Sample Output A-B-C | D-E-F
Starter code
"""
Activity: Print Statements and Input/Output
Author: [Your Name]
Course Name: AP Computer Science Principles
Date: M/D/Yr
Description: Practice using f-string, .format(), concatenation, & sep/ end
Language: Python 3.x
"""
# TODO 0) Use input() to ask the user for their name, and store it in a
# variable called name. Then use input() again to ask for their
# age, and store it in a variable called age.
# Remember: input() always returns a string โ you'll need to
# convert age to an int before you can use it in math, but the
# examples below only print it, so str()/f-string formatting
# will handle that part for you.
# TODO 1) f-strings (most readable โ preferred method)
# Use an f-string to print a sentence that includes both
# `name` and `age`.
# TODO 2) format() method
# Print the same sentence as TODO 1, but built with the
# .format() method instead of an f-string.
# TODO 3) Concatenation
# Print the same sentence again, this time using the + operator
# to concatenate strings. Remember: non-string values (like age)
# must be converted with str() before you can concatenate them.
# TODO 4) sep= keyword parameter
# Print the letters "A", "B", and "C" as three separate
# arguments to print(), using sep="-" so the output reads:
# A-B-C
# TODO 5) end= keyword parameter
# Print "Hello" using end="! " so that it does NOT start a new
# line, then print "World" right after it on the same line.
# The combined output should read:
# Hello! World
Requirements
TODO 0) Collect
nameandageusinginput()TODO 1) Print
nameandagein a sentence using an f-stringTODO 2) Print the same sentence using the
.format()methodTODO 3) Print the same sentence using string concatenation (
+), convertingagewithstr()TODO 4) Print
"A","B","C"as separate arguments withsep="-"so the output readsA-B-CTODO 5) Print
"Hello"withend="! ", then print"World"so the output readsHello! Worldon one lineSubmit your .py file and test cases showing that your program worked as intended.
Reflection question
All three of TODO 1โ3 produce the same sentence on screen, but theyโre written three different ways. Which method did you find easiest to read and write, and why do you think f-strings are considered the preferred method in modern Python?
Reminder:
input()always returns astr. You must useint()orfloat()to convert it for math operations.
Assignment: Mad Libs โ String Formatting Showdownยถ
Standards: Output formatting โ f-strings, string concatenation,
.format(),print()sep/endparametersObjective
Students will build a Mad Libsโstyle story generator that collects user input and displays the finished story using four different output-formatting techniques in > > > Python: f-strings, string concatenation, the
.format()method, andprint()โssep/endparameters. By the end of the assignment, students should be able to explain the > tradeoffs between each method and choose the right one for a given situation.Overview
Students write a program that:
Prompts the user for a series of words (nouns, verbs, adjectives, etc.) using
input()Builds a 10-sentence story using those words
Prints the story back to the user โ but very the โblockโ sequence of the story. You must use a different formatting method for each sentence. Consecutive sentences cannot use the same format.
Requirements
Sentences
Required Technique
Notes
1โ4
f-strings
f"You went to the {place}..."1โ2
String concatenation (
+)Must convert non-strings with
str()where needed1โ4
.format()methodUse either positional
{}or named{noun}placeholders1โ2
print()withsepandendBuild the sentence from multiple
print()arguments, not a single string
10 sentences, forming a single coherent(ish) story โ silliness encouraged
At least 8 different user inputs collected (nouns, adjectives, verbs, adverbs, numbers, exclamations, etc.)
Every input variable must be used at least once
Story must include at least one number input, displayed correctly in all four formatting styles at some point (tests
str()/type-awareness)Steps
Plan your story on paper first โ write the 10 sentences with blanks, and decide which 8+ words youโll ask for.
Write all your
input()statements first and store them in clearly named variables.Build the story section by section, following the technique table above.
Test by running the program and filling in silly words โ does the story read correctly and grammatically?
Add the required header block (below) to the top of your file.
Submit your
.pyfile with test cases.Grading Focus
Correct, working use of all four formatting techniques (not just syntactically present โ actually executed correctly)
Appropriate
str()conversions where needed to avoid type errorsCode comments marking each section
Story is complete, coherent, and uses every collected input
Header block present and filled out
Header Block
Place this at the top of your
.pyfile before any code:""" Activity: Mad Libs using f-String Author: [Your Name] Course Name: AP Computer Science Principles Date: M/D/Yr Description: Mad Libs Language: Python 3.x """
MadLibs Example
================================================================================
Activity: Mad Libs using f-String
Author: [EXEMPLAR โ NOT A STUDENT SUBMISSION]
Course Name: AP Computer Science Principles
Date: 2026-08-25
Description: Demonstrates f-strings, string concatenation, the .format()
method, and print() sep/end parameters within one Mad Libs
story. Built to model the REQUIRED FORMAT of the assignment โ
not to be copied.
Language: Python 3.14
================================================================================
**************************** TEACHER NOTE *********************************
This file is an EXEMPLAR only. Students must CANNOT submit this story, these
variable names, or this sentence structure as their own work. Copying this
story (with words swapped) or reusing this exact sentence pattern is a
violation of the assignment's originality requirement โ students must write
their OWN 10-sentence story with their OWN chosen blanks. Use this only to
see how the four formatting techniques should look when executed correctly.
*******************************************************************************
# -----------------------------------------------------------------------
# SECTION 1: Collect user input
# -----------------------------------------------------------------------
name = input("Enter a person's name: ")
place = input("Enter a place: ")
adjective1 = input("Enter an adjective: ")
noun1 = input("Enter a noun: ")
verb1 = input("Enter a verb (past tense): ")
animal = input("Enter an animal: ")
food = input("Enter a food: ")
adjective2 = input("Enter another adjective: ")
number = int(input("Enter a number: "))
exclamation = input("Enter an exclamation (e.g. Wow!): ")
# -----------------------------------------------------------------------
# SECTION 2: The story โ technique VARIES every sentence.
# No two consecutive sentences use the same formatting method:
# Sentence: 1 2 3 4 5 6 7 8 9 10
# Technique: F M F C M P F C M P
# (F = f-string, M = .format(), C = concatenation, P = print sep/end)
# -----------------------------------------------------------------------
# Sentence 1 โ f-string
print(f"Once upon a time, {name} went to {place}.")
# Sentence 2 โ .format()
print("{} was feeling very {} that day, and carried a {}.".format(
name, adjective1, noun1))
# Sentence 3 โ f-string
print(f"Suddenly, {name} {verb1} right past {number} {animal}s crossing the road!")
# Sentence 4 โ string concatenation (+)
line4 = name + " skidded to a stop and shouted, " + '"' + exclamation + '"'
print(line4)
# Sentence 5 โ .format()
print("There were exactly {0} {1}s staring back.".format(number, animal))
# Sentence 6 โ print() with sep and end
print(name, "reached into a bag and pulled out a", food, sep=" ", end=".\n")
# Sentence 7 โ f-string
print(f"The {adjective2} {animal} sniffed the {food} suspiciously.")
# Sentence 8 โ string concatenation (+)
line8 = name + " counted all " + str(number) + " of them twice, just to be sure."
print(line8)
# Sentence 9 โ .format()
print("{} laughed, dropped the {} and ran...".format(name, noun1))
# Sentence 10 โ print() with sep and end
print("all", number, "the way back to", place, sep=" ", end="!\n")
--------------------------------------------------------------------------
SAMPLE RUN (inputs a student might type at each prompt, in order):
--------------------------------------------------------------------------
Enter a person's name: Priya
Enter a place: the grocery store
Enter an adjective: wobbly
Enter a noun: kazoo
Enter a verb (past tense): sprinted
Enter an animal: goose
Enter a food: burrito
Enter another adjective: suspicious
Enter a number: 7
Enter an exclamation (e.g. Wow!): Yikes!
--------------------------------------------------------------------------
RESULTING OUTPUT:
--------------------------------------------------------------------------
Once upon a time, Priya went to the grocery store.
Priya was feeling very wobbly that day, and carried a kazoo.
Suddenly, Priya sprinted right past 7 gooses crossing the road!
Priya skidded to a stop and shouted, "Yikes!"
There were exactly 7 gooses staring back.
Priya reached into a bag and pulled out a burrito.
The suspicious goose sniffed the burrito suspiciously.
Priya counted all 7 of them twice, just to be sure.
Priya laughed, dropped the kazoo and ran...
all 7 the way back to the grocery store!
--------------------------------------------------------------------------
AP CSP: CRD-2.B
Section 2 โ Control Flow: Conditional Blocks and Loopsยถ
๐ AP CSP: AAP-2.E โ Develop algorithms using sequencing, selection, and iteration.
Python Tutor โ Code Visualizerยถ
๐ Python Tutor Code Visualizer
Python Tutor lets you watch your code run one step at a time instead of just seeing the final output. Paste in your code, hit โVisualize Execution,โ and then step forward and backward through each line while a diagram shows exactly whatโs happening behind the scenes โ variable values, list contents, function calls, and how they change on every pass through a loop.
Why itโs useful:
See loops in action โ Watch a variableโs value update on each iteration instead of guessing what it โshouldโ be.
Track variables visually โ Every variable appears in a box with its current value, updated live as the code executes.
Understand function calls โ See a new โframeโ appear on the call stack each time a function is called, and watch it disappear when the function returns.
Debug faster โ When your code isnโt doing what you expect, step through it to find the exact line where things go wrong.
No installation needed โ Runs entirely in your browser.
How to use it:
Paste your Python code into the editor.
Click Visualize Execution.
Use Next > and < Back to step through your program line by line.
Watch the variables and stack frames update in the visualization panel on the right.
Best for: Debugging loops (for/while), tracing small recursive functions, understanding how variables change over time, and figuring out why your code isnโt producing the output you expect.
Want me to tailor this further โ e.g., shorter for a slide, or expanded with a specific example (like tracing a for loop) matched to a lesson in your PCEP/AP CSP curriculum?
Conditional Statementsยถ
Control flow determines which code runs, when, and how often. Conditionals let the program make decisions.
# if
if condition:
# runs if True
# if-else
if condition:
# runs if True
else:
# runs if False
# if-elif-else
if condition1:
# ...
elif condition2:
# ...
else:
# fallback
๐ Worked Example โ before you build the Grade Calculator below
temperature = int(input("Enter the temperature: ")) if temperature >= 90: print("Heat warning!") elif temperature >= 70: print("Nice day.") elif temperature >= 50: print("A bit cool.") else: print("Bundle up!")Trace it by hand: if
temperatureis65, Python checks>= 90(False), then>= 70(False), then>= 50(True) โ so it prints"A bit cool."and skips every branch after. Only ONE branch of anif-elif-elsechain ever runs.
Example: Grade Calculator
score = int(input("Enter your score: "))
if score >= 90:
print("A")
elif score >= 80:
print("B")
elif score >= 70:
print("C")
elif score >= 60:
print("D")
else:
print("F")
Nested Conditionals
if score >= 70:
if score >= 90:
print("A or A+")
else:
print("Pass")
else:
print("Fail")
๐ก Prefer
elifover deep nesting when possible โ it keeps code readable.
๐ค Thinking Question โ check your answer
Question: In an if-elif-else chain ordered from highest score to lowest (like the Grade Calculator above), what would go wrong if you accidentally wrote the conditions from lowest to highest instead (score >= 60, then score >= 70, etc.)?
Answer: Every score of 60 or above would immediately match the first branch (score >= 60) and print "D", since Python stops at the first True condition in the chain and never checks the rest โ a 95 would incorrectly get a โDโ instead of an โAโ. Order matters in elif chains whenever the conditions overlap.
Activity: Grade Calculatorยถ
Write a grade calculator that accepts a numerical score and outputs:
The letter grade (AโF)
Whether the student passed or failed
A motivational message for scores below 70
Use if-elif-else and at least one nested conditional.
๐งฉ Scaffolding โ build it in this order:
Get the score and print just the letter grade using
if-elif-else.Add the pass/fail message as a separate
if-else(or nest it inside, per the requirements).Add the motivational message only for scores below 70 โ test with a score of exactly 69 and exactly 70 to make sure your boundary is correct.
Starter code:
# Assignment: Program 2: Grade Calculator
# Description: Prints a letter grade, pass/fail status, and a
# motivational message for low scores.
# Language: Python 3.x
score = int(input("Enter your score: "))
# TODO 1) if-elif-else chain for letter grade A-F
# TODO 2) pass/fail message (score >= 60 passes)
# TODO 3) motivational message only if score < 70
AP CSP: AAP-2.E | PCEP: 2.1
Loopsยถ
๐ PCEP 2.2 โ Perform different types of iterations
๐ AP CSP: AAP-2.E โ Iteration; AAP-2.K โ For loops
The for Loop
Structure
for item in iterable:
# code block (loop body)
# runs once per item in the iterable
itemโ a variable created by the loop; holds the current value on each passiterableโ any sequence or object you can step through:range(), a list, a string, a tuple, a dictionary, etc.The body must be indented (4 spaces, PEP 8) โ indentation defines the block
Common Forms
# Iterate over a range of numbers
for i in range(5):
print(i) # 0, 1, 2, 3, 4
# Iterate over a list
fruits = ["apple", "banana", "cherry"]
for fruit in fruits:
print(fruit)
# Iterate with both index and value
for index, fruit in enumerate(fruits):
print(index, fruit)
# Iterate over a string, character by character
for char in "hello":
print(char)
# Iterate over dictionary keys, values, or both
grades = {"Ana": 92, "Ben": 85}
for name, score in grades.items():
print(name, score)
When to Use a for Loop
Use for when you know what youโre iterating over โ a fixed collection, a range of numbers, or anything with a defined, countable set of items. The loop naturally stops when it runs out of items, so you donโt have to manage a stopping condition yourself.
Reach for for when:
Youโre processing every item in a list, string, tuple, or dictionary
You know (or can calculate) exactly how many times to repeat something
Youโre building a new collection from an existing one
You need a counter-controlled loop (
for i in range(n))
Rule of thumb: if you can describe the task as โfor each item in this collectionโฆโ โ itโs a for loop.
PCEP-30-02 / AP CSP Connection
forloops map to the Control Flow exam block (iteration statements)Understanding
range()andenumerate()is directly tested on PCEP-30-02AP CSP frames this as iteration over a defined data set (Big Idea: Algorithms and Programming)
The while Loop
Structure
while condition:
# code block (loop body)
# runs as long as condition is True
# something inside the loop must eventually make condition False
conditionโ any expression that evaluates toTrueorFalseThe loop checks the condition before each pass; if itโs
Falseon the first check, the body never runsThe body must contain something that changes the conditionโs outcome, or the loop never ends (an infinite loop)
Common Forms
# Basic counter-controlled while loop
count = 0
while count < 5:
print(count)
count += 1 # without this line, the loop never ends
# Sentinel-controlled loop (runs until a specific value appears)
response = ""
while response != "quit":
response = input("Type 'quit' to exit: ")
# Flag-controlled loop
running = True
while running:
if some_condition:
running = False
# Intentional infinite loop with a break
while True:
user_input = input("Enter a number (or 'q' to stop): ")
if user_input == "q":
break
print(int(user_input) ** 2)
When to Use a while Loop
Use while when you donโt know in advance how many times the loop needs to run โ the loop depends on a condition that changes based on user input, external data, or logic evaluated during execution.
Reach for while when:
Youโre waiting for a specific user input (validation loops, menus)
Youโre repeating until some condition in your programโs state becomes true/false
You donโt have a fixed collection to iterate over
You need to keep going until an event happens, not until you run out of items
Rule of thumb: if you can describe the task as โkeep doing this untilโฆโ โ itโs a while loop.
for vs. while โ Quick Comparisonยถ
Situation |
Use |
|---|---|
Known number of repetitions |
|
Iterating over a collection |
|
Repeating until a condition changes |
|
Validating user input |
|
Counter-controlled with a fixed range |
|
Unknown/variable number of repetitions |
|
PCEP-30-02 / AP CSP Connection
whileloops fall under the Control Flow exam block, alongside conditional statementsPCEP-30-02 tests infinite loop recognition and
break/continueinteraction withwhileAP CSP frames this as iteration controlled by a Boolean condition (Big Idea: Algorithms and Programming)
Loop Control Keywords
Keyword |
Purpose |
Example |
|---|---|---|
|
Exit loop immediately |
|
|
Skip to next iteration |
|
|
Placeholder โ does nothing |
|
|
Runs if loop ends without |
|
for...else and while...else
Python allows an else clause on both for and while loops โ a feature many languages donโt have. Itโs easy to misread, so this page focuses on exactly when the else block runs.
The Rule
The
elseblock runs only if the loop completes normally โ that is, it runs to the end without hitting abreak.
If a break statement fires, the else block is skipped. If the loop finishes on its own (or never runs at all, in the case of while), the else block executes.
for...else Structure
for item in iterable:
# loop body
if some_condition:
break
else:
# runs only if the loop never hit 'break'
pass
Example โ Searching for a Value
numbers = [4, 7, 11, 2, 9]
target = 15
for n in numbers:
if n == target:
print("Found it!")
break
else:
print("Target not found in the list.")
Here, the loop checks every number, never finds 15, never breaks โ so the else block runs and prints "Target not found in the list."
while...else Structure
while condition:
# loop body
if some_condition:
break
else:
# runs only if the loop exited because condition became False
# (not because of a break)
pass
Example โ Countdown with a Cancel Option
count = 5
while count > 0:
print(count)
if count == 3:
cancel = input("Type 'stop' to cancel: ")
if cancel == "stop":
break
count -= 1
else:
print("Countdown finished normally!")
If the user types "stop", the break fires and "Countdown finished normally!" never prints. If they donโt, the loop runs its course and the else block executes.
Why This Trips People Up
The keyword else here does not mean โotherwise, if the condition was falseโ the way it does with if. It means:
โRun this if the loop was not interrupted by a
break.โ
A helpful mental rewrite: think of it as nobreak instead of else โ thatโs literally what it checks for.
When to Use It
Searching a collection for a match, where you want a โnot foundโ message only if you never broke out early (classic
for...elseuse case)Validating input in a loop, where you want a success message only if the loop wasnโt cancelled by a
breakAnytime youโd otherwise use a separate flag variable (
found = False) just to check afterward whether abreakhappened โ theelseclause replaces that flag
Rule of thumb: if your instinct is to set a boolean flag before the loop and check it after, thatโs a strong sign a for...else or while...else could replace it.
PCEP-30-02 / AP CSP Connection
The
for...else/while...elseconstruct is explicitly tested on PCEP-30-02 under the Control Flow block โ expect questions asking whether theelseblock executes given a specificbreakplacementThis is a Python-specific feature with no direct AP CSP pseudocode equivalent; frame it for students as โPythonโs built-in flag variableโ
Nested Loops
Nested for Loops
Structure
for outer_item in outer_iterable:
# outer loop body
for inner_item in inner_iterable:
# inner loop body
# runs completely, start to finish, for EVERY pass of the outer loop
The outer loop controls how many times the entire inner loop runs
The inner loop runs all the way through before the outer loop advances to its next item
Total iterations =
(outer iterations) ร (inner iterations)
Common Forms
# Basic nested loop โ multiplication table
for i in range(1, 4):
for j in range(1, 4):
print(i * j, end=" ")
print() # newline after each row
# Nested loop over two lists โ all combinations (pairs)
colors = ["red", "blue"]
sizes = ["S", "M", "L"]
for color in colors:
for size in sizes:
print(f"{color} - {size}")
# Nested loop over a 2D structure (list of lists / grid)
grid = [[1, 2, 3], [4, 5, 6], [7, 8, 9]]
for row in grid:
for value in row:
print(value, end=" ")
print()
# Nested loop building a pattern
for row in range(5):
for col in range(row + 1):
print("*", end="")
print()
Tracing Execution โ Why Order Matters
for i in range(2): # outer: runs 2 times
for j in range(3): # inner: runs 3 times PER outer pass
print(i, j)
Output:
0 0
0 1
0 2
1 0
1 1
1 2
The inner loop resets and runs fully every time the outer loop advances โ this is the single most important thing to trace correctly when reading nested loop code.
break and continue in Nested Loopsยถ
A break or continue inside the inner loop only affects the inner loop โ it has no effect on the outer loop.
for i in range(3):
for j in range(3):
if j == 1:
break # exits only the inner loop
print(i, j)
There is no built-in way to break out of both loops at once; common workarounds are a flag variable, a function with return, or restructuring the logic.
When to Use Nested for Loops
Reach for nested loops when:
Working with 2D data: grids, matrices, tables, game boards
Generating all combinations/pairs between two collections
Building patterns where each row depends on a repeated inner sequence
Comparing every item to every other item in a collection
Caution: nested loops multiply your run time (O(nยฒ) for two nested loops over n items). For large datasets, check whether a single loop, a dictionary lookup, or a library function (e.g. itertools) can replace one of the loops before defaulting to nesting.
PCEP-30-02 / AP CSP Connection
Nested loops fall under Control Flow, and PCEP-30-02 frequently tests tracing nested loop output (predict-the-output questions)
AP CSP frames nested iteration in the context of 2D lists / data structures (Big Idea: Algorithms and Programming) and in image/grid manipulation tasks
๐ค Thinking Question โ check your answer
Question: Whatโs the difference between break and continue inside a loop thatโs printing only even numbers from a list?
Answer: continue skips just the current iteration and moves on to the next item โ the loop keeps going. break exits the loop entirely, even if there were more items left to check. If you used break instead of continue the moment you hit an odd number, the loop would stop checking the rest of the list entirely, missing any even numbers that came after it.
Activity: Nested Loop Pattern Design Studioยถ
A single loop can only move in one direction. The moment a shape has both rows and columns โ a triangle, a diamond, a hollow box โ you need a loop inside a loop. The outer loop picks the row; the inner loop decides what happens across that row.
Warm-up โ reading a pattern like a programmer. Before writing code, answer these for the pattern below:
How many rows are there?
Row by row, what changes? (number of characters, starting character, spacing)
Is there a pattern to the pattern? (e.g., โrow
iprintsistarsโ)Is it solid (every position filled) or hollow (only a border filled)?
*
* *
* * *
* * * *
* * * * *
๐ Worked Example โ nested loops in a different context (seating chart labels)
rows = 3 seats_per_row = 4 for row in range(1, rows + 1): for seat in range(1, seats_per_row + 1): print(f"R{row}S{seat}", end=" ") print()Output:
R1S1 R1S2 R1S3 R1S4 R2S1 R2S2 R2S3 R2S4 R3S1 R3S2 R3S3 R3S4Trace it: the outer loop (
row) picks which row youโre on, and the inner loop (seat) does something once per column within that row โ the same outer/inner relationship youโll need for the shape patterns below, just applied to seat labels instead of a printed shape.
Guided example โ hollow rectangle (Tier 2 difficulty):
rows = int(input("Enter the number of rows: "))
cols = int(input("Enter the number of columns: "))
for i in range(1, rows + 1):
for j in range(1, cols + 1):
if i == 1 or i == rows or j == 1 or j == cols:
print("*", end="")
else:
print(" ", end="")
print()
Discussion: What single condition controls whether the shape is solid or hollow? (The if/else inside the inner loop โ remove it and the shape becomes solid.)
The Studio Project โ Design Your Own
Design and code 4 original patterns. โOriginalโ means not shown in class, not copied from a classmate. > For each pattern, choose one option from each category below (no repeating the exact same combination twice):
Category
Options
Fill type
Solid / Hollow
Content
ASCII character (your choice) / Sequential numbers / Repeating digit tied to row number
Orientation
Grows then stays / Grows then shrinks (diamond/hourglass) / Right-aligned / Shifts diagonally (parallelogram)
Input
At least one of your 4 patterns must accept user input for size
๐งฉ Scaffolding โ suggested difficulty progression (do them in this order):
Tier 1 โ Solid, fixed growth (e.g., a solid right triangle โ see Worked Example above)
Tier 2 โ Hollow (border-only conditional logic โ see Guided Example above)
Tier 3 โ Numeric (a value that changes per row)
Tier 4 โ Your choice / mirrored or diagonal shape
Think through the process (before coding): sketch the pattern on grid paper, answer the four warm-up questions for your own design, and write pseudocode for the outer loop, inner loop, and the row-vs-column relationship โ then code it.
Deliverable: 4 working programs, final code, and one paragraph explaining what the outer loop controls vs. what the inner loop controls.
Rubric (8 pts per pattern, 32 pts total):
Criteria
Points
Sketch and pseudocode completed before code, and match the final output
2
Pattern runs without errors and matches the intended design
2
Nested loop logic is correct (not hard-coded repeated
print()statements)2
At least one pattern correctly uses user input to control size
1
Written explanation correctly identifies the role of outer vs. inner loop
1
AP CSP: AAP-2.E | PCEP: 2.2
Project: Loop Concepts Activity Projectยถ
These three activities, isolate each loop concept while layering
if/elif/elsefor decision logic. Redundancy across activities is intentional โ you will see the same control-flow ideas resurface in a new pathway context. Read through each project idea. Choose 1 idea that you would like to develop.
Activity
Concept Focus
Pathway
1 โ Bridge Load Capacity Simulator
forloop,whileloopEngineering
2 โ Network Intrusion Scanner
for...else,while...elseCS / Cybersecurity
3 โ Petri Dish Contamination Scan
nested
for,break,continueBio-Technology
= = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = =
Activity 1: Bridge Load Capacity Simulator
Concept:
forloop vs.whileloop solving related problems,if/elif/elseScenario: Students are structural engineers running a virtual stress test on a bridge design. The bridge has a maximum rated capacity. Part A runs a fixed, known number of standardized load tests (a job for
for). Part B keeps adding load until failure, an unknown number of steps (a job forwhile) โ this contrast is the whole point of the activity.""" Activity: Bridge Load Capacity Simulator Author: [Your Name] Course Name: AP Computer Science Principles Date: M/D/Yr Description: Simulate a structural stress test on a bridge design using both a for loop (fixed round of standardized tests) and a while loop (load applied until failure), classifying each reading with if/elif/else. Language: Python 3.x """ MAX_CAPACITY_LBS = 10000 # bridge fails at or above this load LOAD_STEP_LBS = 750 # how much load is added each round def classify_load(current_load, max_capacity): """Return a status string based on % of capacity used.""" percent_used = (current_load / max_capacity) * 100 # TODO: use if/elif/else to return one of: # "SAFE" -> percent_used < 60 # "WARNING" -> 60 <= percent_used < 90 # "CRITICAL" -> percent_used >= 90 pass def run_standardized_tests(num_tests, max_capacity): """ Part A - FOR loop Run a FIXED number of standardized load tests (num_tests rounds), increasing load by LOAD_STEP_LBS each round. Print the round number, current load, and status (via classify_load) for each test. """ # TODO: for loop, exactly num_tests iterations pass def run_to_failure(max_capacity): """ Part B - WHILE loop Keep adding LOAD_STEP_LBS until current load meets or exceeds max_capacity. We don't know in advance how many rounds this takes โ that's why it can't be a for loop. Return the number of rounds it took and the final load applied. """ # TODO: while loop, condition based on max_capacity pass def main(): print("=== PART A: Standardized Test Battery (for loop) ===") run_standardized_tests(5, MAX_CAPACITY_LBS) print("\n=== PART B: Load-to-Failure Test (while loop) ===") rounds, final_load = run_to_failure(MAX_CAPACITY_LBS) print(f"Bridge failed after {rounds} rounds at {final_load} lbs.") if __name__ == "__main__": main()Sample expected output (Part A, first 2 lines):
Round 1: Load = 750 lbs (7.5%) -> SAFE Round 2: Load = 1500 lbs (15.0%) -> SAFEExtension: Can Part A be forced into a
whileloop but Part B cannot be written cleanly as aforloop without first calculating the answer? i.e., why the tool > should match the problem.= = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = =
Activity 2: Network Intrusion Scanner
Concept:
for...else,while...else, nestedifScenario: Students act as security analysts. The
elseclause on a loop only runs if the loop finished without hitting abreakโ thatโs the exact semantics of โscan completed cleanโ vs. โscan interrupted because we found something.โ This activity forces students to feel that distinction rather than memorize it.""" Activity: Network Intrusion Scanner Author: [Your Name] Course Name: AP Computer Science Principles Date: M/D/Yr Description: Use for...else to scan connection logs for a blacklisted IP, and while...else to simulate a limited-attempt access code check, demonstrating that the else clause only fires when no break occurs. Language: Python 3.x """ BLACKLISTED_IPS = ["10.0.0.13", "192.168.1.66", "172.16.0.99"] CONNECTION_LOG = [ "203.0.113.5", "198.51.100.2", "10.0.0.13", "203.0.113.9" ] VALID_ACCESS_CODE = "7734" MAX_ATTEMPTS = 3 def scan_connection_log(log, blacklist): """ FOR...ELSE Walk through each IP in the log. If a blacklisted IP is found, print an ALERT and break immediately (no need to keep scanning). If the loop finishes without ever breaking, the else clause runs and should print that the log is clean. Bonus: use a nested if to classify severity -- if the matched IP is the FIRST item in blacklist, treat it as "CRITICAL", otherwise "HIGH". """ # TODO: for ip in log: ... break ... else: ... pass def check_access_code(get_attempt_func, valid_code, max_attempts): """ WHILE...ELSE Allow up to max_attempts guesses (get_attempt_func() returns the next guess string -- already provided for you, don't rewrite it). Break out as soon as the correct code is entered. If the while condition becomes false (attempts run out) without ever finding the right code, the else clause should print "ACCESS DENIED." """ # TODO: while attempts_used < max_attempts: ... break ... else: ... pass def main(): print("=== Scanning connection log ===") scan_connection_log(CONNECTION_LOG, BLACKLISTED_IPS) print("\n=== Access code check ===") fake_attempts = iter(["1111", "2222", "7734"]) check_access_code(lambda: next(fake_attempts), VALID_ACCESS_CODE, MAX_ATTEMPTS) if __name__ == "__main__": main()Sample expected output:
=== Scanning connection log === ALERT: Blacklisted IP detected -> 10.0.0.13 (CRITICAL) === Access code check === Attempt 1: 1111 -- incorrect Attempt 2: 2222 -- incorrect Attempt 3: 7734 -- ACCESS GRANTEDExtension: Change
fake_attemptsso all three guesses are wrong and predict โ before running โ which branch (breakbody orelsebody) will fire, then verify.= = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = =
Activity 3: Petri Dish Contamination Scan
Concept: nested
forloops,break,continueScenario: Students act as lab techs scanning a grid of petri dish samples (rows x columns). Each cell holds a reading:
0= clean, a positive number = contamination level.continueskips clean cells (nothing to report).breakstops scanning the current row early once 2 contaminated cells are found in it (containment protocol โ no need to keep checking that row).""" Activity: Petri Dish Contamination Scan Author: [Your Name] Course Name: AP Computer Science Principles Date: M/D/Yr Description: Scan a 2D grid of petri dish sample readings with nested for loops, using continue to skip clean cells and break to halt a row early once a containment threshold is reached. Language: Python 3.x """ # Each row is one sample strip; each value is a contamination reading. # 0 = clean. Positive values = contamination level. SAMPLE_GRID = [ [0, 0, 3, 0, 6], [0, 2, 0, 5, 9], [0, 0, 0, 0, 0], [4, 0, 8, 1, 0], ] ROW_CONTAINMENT_LIMIT = 2 # stop scanning a row after this many hits def classify_reading(level): """Nested if/elif/else severity classification.""" # TODO: # level == 0 -> "clean" # 1 <= level <= 4 -> "low" # 5 <= level <= 8 -> "moderate" # level >= 9 -> "severe" pass def scan_grid(grid, limit): """ Outer for loop -> each row (a sample strip) Inner for loop -> each cell in that row - If a cell is clean (0), `continue` to the next cell immediately. - Otherwise print row/col position and severity via classify_reading. - Track how many contaminated cells found THIS row; once it hits `limit`, print a containment message and `break` out of the inner loop (stop scanning that row, move on to the next row). """ # TODO: nested for loops with continue and break pass def main(): scan_grid(SAMPLE_GRID, ROW_CONTAINMENT_LIMIT) if __name__ == "__main__": main()Sample expected output (first row only):
Row 0: Col 2: level 3 -> low Col 4: level 6 -> moderate Containment limit reached on Row 0 -- halting scan of this row.Extension (harder): Add an outer
breakโ if any single cell reads>= 9(โsevereโ), halt the entire scan immediately (whole dish is compromised), not just the > current row. This requires either a flag variable or restructuring into a function thatreturns early โ a good bridge to whybreakalone canโt exit two loops at once.
Loop Concepts Activity Project โ Grading Rubric
Click Here for Project Rubric
Standards Alignment Reference
Code |
Standard |
Where it shows up in this project |
|---|---|---|
|
Develop algorithms using sequencing, selection, and iteration |
Core loop + |
|
For loops |
Activity 1 Part A; Activity 2 log scan; Activity 3 outer/inner loops |
|
Use procedures/functions to manage complexity |
Function decomposition ( |
|
Implement algorithms in a programming language |
Overall program implementation |
|
Identify, test, and correct errors in algorithms and programs |
Test cases / sample runs; boundary conditions |
|
Conditional statements |
|
|
Perform different types of iterations |
|
|
Decompose code using functions |
Functions with docstrings, single responsibility |
Applies to the following projects:
Activity 1 (Bridge Load Capacity Simulator)
Activity 2 (Network Intrusion Scanner)
Activity 3 (Petri Dish Contamination Scan)
Total: 40 points โ 24 pts Common Criteria (all activities) + 16 pts Concept-Specific Core Logic (grade only the row matching the studentโs chosen activity).
Common Criteria (24 pts โ all activities)
Criteria |
Points |
|---|---|
Header block complete (Assignment, Author, Description, etc.) and PEP 8 style followed |
3 |
Program runs without crashing on normal input |
3 |
Logic correctly decomposed into functions (no core logic crammed into |
3 |
Custom functions include docstrings describing their purpose |
2 |
|
4 |
Output format matches the sample output shown for the chosen activity (spacing, labels, rounding) |
4 |
Test cases / sample runs submitted, including at least one boundary or edge case (not just a โhappy pathโ run) |
4 |
Code comments explain what the loop is doing and why at each key step, not just restating the code |
1 |
Concept-Specific Core Logic (16 pts โ score only the row that matches)
Activity 1 โ Bridge Load Capacity Simulator (for vs. while)
Criteria |
Points |
|---|---|
|
4 |
|
4 |
Both functions correctly call |
4 |
Extension question answered correctly: explains why Part A could be forced into |
4 |
Activity 2 โ Network Intrusion Scanner (for...else, while...else)
Criteria |
Points |
|---|---|
|
6 |
|
6 |
Nested |
2 |
Extension question answered correctly: predicts, before running, which branch ( |
2 |
Activity 3 โ Petri Dish Contamination Scan (nested for, break, continue)
Criteria |
Points |
|---|---|
|
4 |
|
4 |
|
4 |
|
2 |
Extension (harder) attempted: an outer break/flag or restructured function correctly halts the entire scan on a severe ( |
2 |
Scoring Summary
Section |
Points |
|---|---|
Common Criteria |
24 |
Concept-Specific Core Logic (chosen activity only) |
16 |
Total |
40 |
Section 3 โ Data Collections: Lists, Tuples, Dictionaries, and Stringsยถ
๐ AP CSP: AAP-4.A โ Use data abstractions to manage complexity.
Stringsยถ
๐ PCEP 3.4 โ Operate with strings ๐ AP CSP: DAT-1.A
Strings are ordered, immutable sequences of characters.
greeting = "Hello, World!"
print(greeting[0]) # H (indexing)
print(greeting[-1]) # ! (negative index)
print(greeting[0:5]) # Hello (slicing)
print(greeting[::-1]) # !dlroW ,olleH (reverse)
print(len(greeting)) # 13
String Indexing Reference
string = | P | O | T | A | T | O |
pos_index: 0 1 2 3 4 5
neg_index: -6 -5 -4 -3 -2 -1
Escape Characters
print("She said \"Hello\"") # She said "Hello"
print("Line 1\nLine 2") # newline
print("Col1\tCol2") # tab
print("Backslash: \\") # \
Multi-line Strings
poem = """
Roses are red,
Violets are blue.
"""
Beginner String Methods
Method |
Description |
Example |
|---|---|---|
|
Lowercase |
|
|
Uppercase |
|
|
Remove whitespace |
|
|
Replace text |
|
|
Split into list |
|
|
Length |
|
|
Index of substring |
|
|
Count occurrences |
|
|
Starts with? |
|
|
Ends with? |
|
|
All letters? |
|
|
All digits? |
|
|
Capitalize the first letter of each word |
โmarco reyesโ.title() โ โMarco Reyesโ |
๐ค Thinking Question โ check your answer
Question: name = " Ada Lovelace ". What does name.strip().upper() return, and why does the order of the two method calls matter here?
Answer: "ADA LOVELACE". .strip() removes the leading/trailing whitespace first, then .upper() capitalizes the result. Because strings are immutable, each method returns a new string rather than modifying name in place โ thatโs why you can โchainโ methods like this, each one operating on the result of the one before it. Order rarely matters for .strip()/.upper() specifically, but it does for methods that depend on exact spacing, like .startswith().
๐ Worked Example โ string methods in a different context (library due-date slip)
book_title = " the hobbit " library_name = "westview public library" due_date = "09/15" clean_title = book_title.strip().title() # "The Hobbit" header = library_name.upper() # "WESTVIEW PUBLIC LIBRARY" print(header) print(f"{clean_title}\tDue: {due_date}") print("\"Please return on time to avoid fees.\"")This shows the same techniques the Receipt Formatter asks for โ
.strip()to clean messy input,.upper()/.title()for capitalization,\tfor column alignment, and a quoted message with\"โ applied to a library slip instead of a receipt.
Practice Drills โ Stringsยถ
Quick single-concept reps, 10โ15 min each โ do these before or alongside Receipt Formatter below.
S1 โ String Slicer
Given phrase = "Computer Science Principles", without hardcoding letters, use slicing/indexing to print: (1) just "Computer", (2) just "Principles", (3) the whole phrase reversed, (4) every other character.
phrase = "Computer Science Principles"
# TODO 1) print "Computer" using slicing
# TODO 2) print "Principles" using slicing
# TODO 3) print the phrase reversed
# TODO 4) print every other character
#############################################################################
S2 โ Name Formatter
Ask the user for their first and last name in one input() call (e.g. "marco reyes"), typed messily (any case, extra spaces). Use .strip(), .split(), and .title() to print it cleanly as "Reyes, Marco".
full_name = input("Enter your first and last name: ")
# TODO 1) strip() the input
# TODO 2) split() into first and last
# TODO 3) print as "Last, First" using .title()
#############################################################################
S3 โ Palindrome Checker
Ask the user for a word. Using slicing ([::-1]) โ no loops needed โ check if it reads the same forwards and backwards. Ignore case.
word = input("Enter a word: ").lower()
# TODO: compare word to its reversed slice, print True/False
#############################################################################
S4 โ Vowel Counter
Ask for a sentence. Count how many vowels (a, e, i, o, u, either case) it contains using .count().
sentence = input("Enter a sentence: ")
# TODO: count vowels using .count() for each vowel letter
#############################################################################
S5 โ Find and Replace Censor
Ask for a sentence and a โbanned word.โ Use .find() to check if the word appears, then .replace() to swap every occurrence with asterisks matching its length (e.g., "spam" โ "****").
sentence = input("Enter a sentence: ")
banned = input("Enter a word to censor: ")
# TODO 1) use .find() to check if banned word is present
# TODO 2) if present, use .replace() to swap it with "*" * len(banned)
#############################################################################
S6 โ Acronym Generator
Ask for a multi-word phrase (e.g., "as soon as possible"). Use .split() to get the words, grab the first letter of each with a loop or comprehension, and .join() them into an uppercase acronym.
phrase = input("Enter a phrase: ")
# TODO: split the phrase into words, take the first letter of each,
# join them into an uppercase acronym
Sample: "as soon as possible" โ ASAP
#############################################################################
S7 โ Pig Latin Translator
Pig Latin Translator**
Ask the user for a full sentence. Use .split() to break it into words, translate each word to Pig Latin, then .join() them back into a sentence:
If a word starts with a vowel, add
"way"to the end.If a word starts with a consonant, move just the first letter to the end, then add
"ay".
sentence = input("Enter a sentence: ").lower()
vowels = "aeiou"
# TODO 1) split the sentence into a list of words
# TODO 2) loop through the words; for each word:
# - if word[0] is a vowel: translated = word + "way"
# - if word[0] is a consonant: translated = word[1:] + word[0] + "ay"
# - append translated to a new list
# TODO 3) join the translated words back into a sentence with " "
# TODO 4) print the translated sentence
Sample: "the pig ran" โ "hetay igpay anray"
Extension โ Decode Pig Latin back to English: Given a Pig Latin sentence, translate it back to English. For each word: if it ends in "way", strip "way" off (it started with a vowel). Otherwise, strip "ay" off the end, then move the last remaining letter back to the front (it was the consonant that got moved).
pig_sentence = input("Enter a Pig Latin sentence: ").lower()
# TODO 1) split into words
# TODO 2) loop through the words; for each word:
# - if it ends with "way": original = word without the "way"
# - otherwise: strip the "ay", then move the LAST letter of
# what's left back to the front:
# original = remainder[-1] + remainder[:-1]
# TODO 3) join and print the decoded sentence
Try it: decode "hetay igpay anray" โ you should get back "the pig ran".
๐ค Discussion: Try encoding a word that starts with the letter w, like "wave" โ "aveway". Now try decoding "aveway" โ does it come back as "wave"? (It wonโt! The decoder sees it ends in "way" and assumes it started with a vowel, since thatโs the exact same ending a consonant-w word produces. This is a nice bug to trace by hand: a single moved letter can accidentally recreate the โstarted with a vowelโ signal. Real ciphers need to guarantee their encoding is unambiguous to decode โ this is why, in the XOR cipher extension from the steganography lab, we cared about avoiding collisions too.)
#############################################################################
Submit your .py file and test cases showing that your program worked as intended.
# Assignment: String Practice
# Author: [Your Name]
# Course Name: AP Computer Science Principles
# Description: Using string methods to manipulate strings
# Language: Python 3.x
PCEP: 3.4 | AP CSP: DAT-1.A
Activity: Receipt Formatterยถ
Write a program that formats a store receipt using string methods and escape characters.
๐งฉ Scaffolding โ build it in this order:
Get store name, item name, and price from the user; print them raw (unformatted) first to confirm input works.
Add
.upper()for the store header and.title()for the item name.Add the
\t/\nformatting and the quoted thank-you message.Add the
.strip()demonstration and.replace()discount label last โ these are the trickiest requirements.
Requirements:
Ask the user for a store name, an item name, and a price.
Use
.upper()to print the store name in all caps as a header.Use
.title()to properly capitalize the item name (e.g.,"blue notebook"โ"Blue Notebook").Use
\tto align the item name and price in two columns, and\nto add blank lines between sections.Use
\"to print a quoted โThank you for shopping with us!โ message at the bottom.Ask the user to re-enter the item name with extra spaces on purpose (e.g.,
" notebook "), then use.strip()to clean it before printing โ show the before and after to prove the method worked.Use
.replace()to apply a discount label โ replace"Price:"with"Sale Price:"in your printed line if the price is above $20.Bonus: use
.startswith()or.endswith()to check if the item name starts with a vowel, and print a fun fact if it does.
Sample interaction:
Enter store name: target
Enter item name: blue notebook
Enter price: 24.99
TARGET
Blue Notebook Sale Price: $24.99
"Thank you for shopping with us!"
PCEP: 3.4 | AP CSP: DAT-1.A
Listsยถ
๐ PCEP 3.1 โ Collect and process data using lists ๐ AP CSP: AAP-4.A โ Lists for data abstraction
A list is an ordered, mutable sequence. It is the most versatile data collection in Python โ and the collection type the AP CSP CPT specifically requires you to use.
fruits = ["apple", "banana", "cherry"]
print(fruits[0]) # apple
print(fruits[-1]) # cherry
print(fruits[1:3]) # ['banana', 'cherry']
Common List Methods
fruits.append("orange") # Add to end
fruits.insert(1, "blueberry") # Insert at index
fruits.remove("banana") # Remove by value
popped = fruits.pop() # Remove & return last item
fruits.sort() # Sort in place
fruits.reverse() # Reverse in place
print(fruits.index("apple")) # Find index
print(fruits.count("apple")) # Count occurrences
fruits2 = fruits.copy() # Clone the list
fruits.clear() # Remove all items
Iterating Through Lists
for item in fruits:
print(item)
for i, item in enumerate(fruits):
print(f"{i}: {item}")
Enumerate Explained
enumerate(fruits)
fruits is a list, like ["apple", "banana", "cherry"]. Normally when you loop through a list, you just get each item โ you donโt automatically know its position (index). enumerate() fixes that: it goes through the list and gives you two things at once for each item โ its position number and the value itself.
for i, item in enumerate(fruits):
This loop grabs both of those things each time around:
i= the index (position), starting at 0item= the actual fruit at that position
So for ["apple", "banana", "cherry"], the loop runs three times:
i |
item |
|---|---|
0 |
apple |
1 |
banana |
2 |
cherry |
print(f"{i}: {item}")
This is an f-string โ a way to insert variables directly into a string. It prints the index, a colon, then the item.
Putting it together, the output would be:
0: apple
1: banana
2: cherry
Why use enumerate instead of just for item in fruits?
Sometimes you need the position too โ like if you want to number a list for students, or you need to know โthis is the 3rd itemโ while also using its value. Without enumerate, youโd have to manually track a counter variable yourself (i = 0, then i += 1 each loop) โ enumerate does that bookkeeping for you.
enumerate hands you a stack of index cards, each one labeled with a number and taped to a fruit โ you just read off both labels as you go through the stack.
in and not in Operators
print("apple" in fruits) # True
print("grape" not in fruits) # True
List Comprehensions
A list comprehension is a compact way to build a new list by looping over an existing sequence โ it packs a for loop (and optionally an if condition) into a single line. It always follows the same shape:
new_list = [expression for item in sequence if condition]
expressionโ what to do with each item before adding it to the new listfor item in sequenceโ the loop, exactly like a normalforloopif condition(optional) โ only include the item if this isTrue
The traditional loop version and the comprehension version produce the exact same result โ the comprehension is just shorter:
# Traditional loop
squares = []
for x in range(10):
squares.append(x ** 2)
# Equivalent list comprehension
squares = [x ** 2 for x in range(10)]
squares = [x ** 2 for x in range(10)]
evens = [x for x in range(20) if x % 2 == 0]
upper_words = [word.upper() for word in ["hi", "bye"]]
==When to use which: list comprehensions are great for simple, one-line transformations. If your loop needs multiple steps,
print()statements along the way, or complex logic, a traditionalforloop is usually more readable โ donโt force a comprehension just because itโs shorter. Readability counts, per PEP 8.==
Copying vs. Cloning
original = [1, 2, 3]
alias = original # NOT a copy โ both point to same list
clone = original.copy() # Independent copy
clone2 = original[:] # Also a copy (slicing)
2D Lists (Matrices)
grid = [
[1, 2, 3],
[4, 5, 6],
[7, 8, 9]
]
print(grid[1][2]) # 6 (row 1, col 2)
# Iterate a 2D list
for row in grid:
for val in row:
print(val, end=" ")
print()
๐ค Thinking Question โ check your answer
Question: original = [1, 2, 3] then alias = original then alias.append(4). What does print(original) show, and why?
Answer: [1, 2, 3, 4]. alias = original did not create a copy โ it made alias point to the exact same list object in memory as original. Modifying alias (with .append()) modifies the one and only list both names refer to. To get an independent copy, youโd need alias = original.copy() or alias = original[:]. This is the same idea as the is vs == distinction from Section 1.
Practice Drills โ Listsยถ
Quick single-concept reps, 10โ15 min each โ this is the Week 7 activity block.
L1 โ Shopping List Builder
Start with an empty list. Use .append() to add 5 grocery items one at a time (hardcoded, not input). Then: .insert() a forgotten item at index 0, .remove() one item by name, and print the final list.
groceries = []
# TODO 1) append 5 items
# TODO 2) insert a 6th item at the front
# TODO 3) remove one item by name
# TODO 4) print the final list
L2 โ List Stats (No Built-ins)
Given a hardcoded list of test scores, find the highest, lowest, and average score using a for loop and running variables โ without using Pythonโs built-in max(), min(), or sum() functions.
scores = [88, 95, 72, 100, 64, 91]
# TODO 1) loop through scores to find the highest (no max())
# TODO 2) loop through scores to find the lowest (no min())
# TODO 3) loop through scores to compute the average (no sum())
L3 โ Duplicate Remover
Given a list with repeated values, build a new list containing only the first occurrence of each value (preserve original order โ donโt just use set(), since sets donโt preserve order). Use in/not in to check membership as you build the new list.
raw = [3, 1, 4, 1, 5, 9, 2, 6, 5, 3, 5]
# TODO: build "unique" list containing each value only once, in
# first-seen order
L4 โ List Slicing Practice
Given numbers = [10, 20, 30, 40, 50, 60, 70, 80, 90, 100], use slicing (no loops) to print: (1) the first three numbers, (2) the last three numbers, (3) every other number starting from index 0, (4) the list reversed.
numbers = [10, 20, 30, 40, 50, 60, 70, 80, 90, 100]
# TODO 1-4: four print statements, each using a different slice
L5 โ High Score Tracker
Start with a list of 5 hardcoded high scores. Add a new score with .append(), then use .sort(reverse=True) to rank them highest-to-lowest. Use .pop() to drop the lowest score off the list (keeping only the top 5), and print the final ranked list with placements ("1st: 100", etc.) using enumerate().
high_scores = [72, 88, 95, 61, 84]
# TODO 1) append a new score (e.g., 90)
# TODO 2) sort descending
# TODO 3) pop the lowest score off the end
# TODO 4) print ranked with enumerate() -- "1st: 95", "2nd: 90", etc.
L6 โ 2D List Grid Scanner
Given a 3x3 grid (list of lists) of numbers, use nested loops to find the total sum of all values and print each row on its own line.
grid = [
[4, 8, 1],
[6, 2, 9],
[3, 5, 7]
]
# TODO 1) print each row on its own line
# TODO 2) compute and print the total sum of every value in the grid
L7 โ Grade Curve Adjuster
Given a list of test scores, use a list comprehension to add 5 bonus points to every score โ but cap any result at 100 (no score can exceed it). This combines list comprehensions with a conditional expression inside the expression part.
scores = [78, 95, 88, 99, 62]
# TODO: build a new list "curved" where each score gets +5,
# capped at 100 (hint: use "x if condition else y" inside
# the comprehension's expression)
Sample: [78, 95, 88, 99, 62] โ [83, 100, 93, 100, 67]
PCEP: 3.1 | AP CSP: AAP-4.A
#############################################################################
Activity: Playlist Managerยถ
Unit: Data Collections (Lists) โ PCEP-30-02 Block 3 / AP CSP Big Idea 3 Prerequisite: Strings unit / Receipt Formatter activity
Write a program that manipulates a premade list of songs using list methods and slicing.
Starter list (give this to students โ donโt let them type it in):
playlist = ["Blinding Lights", "Levitating", "As It Was", "Flowers", "Anti-Hero",
"Unholy", "Cruel Summer", "Vampire", "Espresso", "Paint The Town Red",
"Snooze", "Lovin On Me", "Fortnight", "Please Please Please", "Birds Of A Feather",
"Not Like Us", "Texas Hold Em", "I Had Some Help", "Good Luck Babe!", "Beautiful Things"]
#############################################################################
๐งฉ Scaffolding โ build it in this orderยถ
Print the raw list, its
len(), and the first/last song using indexing โ confirm the list loads and indexing works before doing anything fancy.Use slicing to print the โTop 5โ (
playlist[:5]) and the โBottom 5โ (playlist[-5:]).Add songs with
.append()and.insert()โ one at the end, one at a specific chart position.Add
.remove()and.pop()โ one that removes by name, one that removes by position and keeps the removed value in a variable to print (โRemoved from playlist: โฆโ).Add
.sort()(alphabetical) and.sort(reverse=True), printing the list each time so students see it mutate in place.Add
.index(),.count(), and theinkeyword last โ these require the trickiest thinking (what happens if the song isnโt there?).
#############################################################################
Requirementsยถ
Start from the given
playlistlist. Print it, its length, and the song at index0and index-1.Use slicing to display a โTop 5โ and a โBottom 5โ sublist without modifying the original list.
Ask the user for a new song to add. Use
.append()to add it to the end, then use.insert()to add a second new song at position3(like inserting it into the #4 chart slot).Ask the user which song to remove. Use
.remove()to take it out by name โ wrap it so the program doesnโt crash if the song isnโt in the list (if song in playlist:).Use
.pop(0)to remove the current #1 song, store it in a variable, and print"Dropped from #1: <song>".Use
.sort()to alphabetize the list and print it, then use.sort(reverse=True)and print it again โ label each printout so itโs clear which order is which.Ask the user for a song title and use
.index()to report its chart position (add 1 so it reads as a human rank, not a 0-based index) โ handle the case where it isnโt found instead of crashing.Use
.count()to check whether the user accidentally added a duplicate song, and print a warning ifcount > 1.Use
len()one more time at the end to print the final total number of songs on the playlist.Bonus: Use
.reverse()to flip the current order in place (different fromsort(reverse=True)โ ask them to explain the difference in a comment).Bonus: Use a list comprehension to print only the songs that contain the word โLoveโ (case-insensitive) or another keyword of their choice.
#############################################################################
Sample interactionยถ
--- Current Playlist ---
['Blinding Lights', 'Levitating', 'As It Was', ... ]
Total songs: 20
#1: Blinding Lights
Last song: Beautiful Things
Top 5: ['Blinding Lights', 'Levitating', 'As It Was', 'Flowers', 'Anti-Hero']
Bottom 5: ['Not Like Us', 'Texas Hold Em', 'I Had Some Help', 'Good Luck Babe!', 'Beautiful Things']
Enter a new song to add: Golden
Enter another new song to insert at #4: Die With A Smile
Enter a song to remove: Snooze
Removed "Snooze" from the playlist.
Dropped from #1: Blinding Lights
--- Alphabetical Order ---
[...]
--- Reverse Alphabetical Order ---
[...]
Enter a song to look up: Espresso
"Espresso" is currently ranked #7
Final playlist total: 21 songs
#############################################################################
Grading Rubric (1 pt per standard)ยถ
Requirement |
Points |
|---|---|
Raw list, |
1 |
Slicing โ Top 5 / Bottom 5 |
1 |
|
1 |
|
1 |
|
1 |
|
1 |
|
1 |
|
1 |
|
1 |
Final |
1 |
Bonus: |
+1 |
Bonus: list comprehension keyword filter |
+1 |
Total: 10 pts (12 with bonus)
โถ Common student errors to watch for
.remove()without anincheck โValueErrorcrash if the song isnโt spelled exactly as in the list (case-sensitive).Confusing
.pop()and.remove()โ.pop()takes an index,.remove()takes a value. Students often pass a song name to.pop().Forgetting
.sort()and.reverse()returnNoneโplaylist = playlist.sort()silently setsplaylisttoNone. Emphasize these mutate in place and donโt need reassignment.Off-by-one on
.index()โ.index()is 0-based, so remind them to+1when displaying a โrankโ to the user.Slicing confusion โ
playlist[:5]vsplaylist[5:]โ walk through this on the board if Top 5 / Bottom 5 come out wrong.
#############################################################################
Tuplesยถ
๐ PCEP 3.2 โ Collect and process data using tuples
A tuple is an ordered, immutable sequence โ values cannot be changed after creation.
coords = (40.7128, -74.0060) # GPS: New York City
rgb = (255, 128, 0)
single = (42,) # Note the comma for single-element tuple
print(coords[0]) # 40.7128
print(len(rgb)) # 3
Tuples vs. Lists
Feature |
List |
Tuple |
|---|---|---|
Ordered |
โ |
โ |
Indexed |
โ |
โ |
Mutable |
โ |
โ |
Duplicates |
โ |
โ |
Use when |
Data changes |
Data is fixed |
Lists inside Tuples and Tuples inside Lists
mixed = ([1, 2, 3], [4, 5, 6]) # tuple of lists
nested = [(1, "a"), (2, "b")] # list of tuples
๐ค Thinking Question โ check your answer
Question: Would you store a studentโs GPA history (which changes every semester) in a list or a tuple? What about a single studentโs date of birth (month, day, year)?
Answer: GPA history โ list, because new entries get added over time (mutable data). Date of birth โ tuple, because once set it never changes โ the immutability of a tuple communicates โthis is fixed dataโ to anyone reading your code.
Practice Drills โ Tuplesยถ
Quick single-concept reps, 10โ15 min each โ do these before Student Contact Book below.
T1 โ Coordinate Pair Basics
Store a GPS coordinate as a tuple (latitude, longitude). Unpack it into two separate variables in a single line and print a formatted sentence using them.
location = (34.0522, -118.2437)
# TODO: unpack location into lat, lon in one line, then print
# f"Latitude: {lat}, Longitude: {lon}"
T2 โ RGB Color Mixer
Store two colors as RGB tuples, e.g. red = (255, 0, 0). Write a function average_color(c1, c2) that takes two RGB tuples and returns a new tuple representing their averaged color (average each channel, round to an int). Test it by mixing two colors.
red = (255, 0, 0)
blue = (0, 0, 255)
def average_color(c1, c2):
# TODO: return a new tuple with each channel averaged (rounded)
pass
print(average_color(red, blue))
T3 โ Proving Immutability
Create a tuple dimensions = (12, 24). Try to change the first value with dimensions[0] = 15 and run it โ read the error Python gives you. Then write one sentence (as a comment) explaining, in your own words, what the error message means and why lists donโt have this problem.
dimensions = (12, 24)
# TODO 1) uncomment the line below, run it, and read the error
# dimensions[0] = 15
# TODO 2) add a comment explaining the error in your own words
T4 โ List of Tuples: Grade Records
Store 5 students as a list of tuples: (name, grade). Loop through the list and print only the students with a grade of 90 or higher. This is a deliberate contrast to the โlist of dictionariesโ pattern used below in Student Contact Book โ same idea (bundling related data), different structure.
records = [
("Ava", 92), ("Liam", 78), ("Noah", 95),
("Mia", 88), ("Zoe", 91)
]
# TODO: loop through records, unpack each tuple, print names
# with grade >= 90
T5 โ Swap and Min/Max Return
Two parts, both showing what tuples are for: (1) swap two variablesโ values in one line using tuple packing โ no temp variable; (2) write a function min_max(numbers) that returns both the smallest and largest value as a single tuple, then unpack the result at the call site.
a, b = 5, 12
# TODO 1) swap a and b in one line using tuple packing/unpacking
def min_max(numbers):
# TODO 2) return a tuple (smallest, largest)
pass
low, high = min_max([4, 19, 2, 8, 11])
print(low, high)
Sample output: 2 19
PCEP: 3.2
Dictionariesยถ
๐ PCEP 3.3 โ Collect and process data using dictionaries ๐ AP CSP: AAP-3.B โ Use abstractions to organize data.
A dictionary stores key-value pairs. Keys must be unique and immutable. Dictionaries are ordered (Python 3.7+) and mutable.
student = {
"name": "Alice",
"age": 16,
"grade": "A"
}
print(student["name"]) # Alice
student["age"] = 17 # Update value
student["school"] = "CAMS" # Add new key
del student["grade"] # Remove key
Checking for Keys
if "name" in student:
print("Key exists!")
Dictionary Methods
student.keys() # dict_keys(['name', 'age', ...])
student.values() # dict_values(['Alice', 17, ...])
student.items() # dict_items([('name','Alice'), ...])
Iterating
for key, value in student.items():
print(f"{key}: {value}")
List of Dictionaries (real-world pattern)
students = [
{"name": "Alice", "age": 14},
{"name": "Bob", "age": 15},
{"name": "Charlie", "age": 14}
]
for s in students:
print(s["name"])
# Count students age 14
count = sum(1 for s in students if s["age"] == 14)
๐ค Thinking Question โ check your answer
Question: Why is a โlist of dictionariesโ (like students above) usually a better data abstraction for the CPT than several separate parallel lists like names = [...], ages = [...]?
Answer: With parallel lists, names[2] and ages[2] only stay linked to the same student if youโre extremely careful to keep every list in sync โ one mistaken .remove() on just one list breaks the connection. A list of dictionaries keeps each studentโs data bundled together in one object, so thereโs no way for a name and age to get separated. This is exactly the kind of data abstraction the CPT written responses ask you to explain.
๐ Worked Example โ list of dictionaries in a different context (playlist)
playlist = [] new_song = {"title": "Clair de Lune", "artist": "Debussy", "duration": "5:12"} playlist.append(new_song) for song in playlist: if song["title"] == "Clair de Lune": print(f"Found: {song['title']} by {song['artist']} ({song['duration']})")This is the pattern (append a dict to a list, then search with a loop +
if) youโll build into a full menu-driven program below โ just applied to songs instead of contacts.
Practice Drills โ Dictionariesยถ
Quick single-concept reps, 10โ15 min each โ do these before Student Contact Book below.
D1 โ Inventory Tracker
Start with a dictionary of 3 hardcoded inventory items and their quantities ({"pencils": 30, ...}). Add a new item, update an existing quantity, remove one item with del, and print the final dictionary using a for key, value in ...items() loop.
inventory = {"pencils": 30, "notebooks": 12, "erasers": 20}
# TODO 1) add a new item
# TODO 2) update an existing item's quantity
# TODO 3) delete one item
# TODO 4) print every item with a for loop over .items()
D2 โ Word Frequency Counter
Given a sentence, build a dictionary counting how many times each word appears. Use .split() to get the words, and if word in freq: to decide whether to add a new key or increment an existing one.
sentence = "the cat sat on the mat the cat ran"
words = sentence.split()
freq = {}
# TODO: loop through words, building the freq dictionary
D3 โ Safe Key Lookup
Given a dictionary of student grades, ask the user for a name. If the name is a key in the dictionary, print their grade; if not, print "Student not found." โ without letting the program crash with a KeyError. Then repeat the same lookup using .get() with a default value instead of in, and compare the two approaches.
grades = {"Ava": "A", "Liam": "C", "Noah": "B"}
name = input("Enter a student name: ")
# TODO 1) look up name using "in" and if/else
# TODO 2) look up the same name again using .get() with a default
# of "Student not found."
D4 โ Nested Dictionary Mini-Database
Build a dictionary of dictionaries representing a small class roster: each key is a student name, and each value is a dictionary with "grade" and "attendance". Print a formatted line for each student by looping through .items() and accessing the nested values.
roster = {
"Ava": {"grade": "A", "attendance": 0.98},
"Liam": {"grade": "C", "attendance": 0.85},
"Noah": {"grade": "B", "attendance": 0.92}
}
# TODO: loop through roster.items(), print each student's grade
# and attendance as a formatted f-string line
D5 โ Dictionary Comprehension Intro
Given a list of numbers, build a dictionary mapping each number to its square using a dictionary comprehension (parallel structure to the list comprehensions already covered). Then rewrite the same thing as a traditional loop, so you can see both side by side.
numbers = [1, 2, 3, 4, 5]
# TODO 1) build squares_dict using a dictionary comprehension
# {n: n**2 for n in numbers}
# TODO 2) build the same dictionary again using a traditional
# for loop, storing it as squares_dict_loop
D6 โ Merge and Invert
Given two dictionaries of student scores from different quizzes, merge them into one using .update() (later quiz overwrites duplicates). Then build a second dictionary thatโs inverted โ scores as keys, names as values โ using a dictionary comprehension.
quiz1 = {"Ava": 88, "Liam": 91}
quiz2 = {"Liam": 95, "Noah": 79}
# TODO 1) merge quiz2 into quiz1 using .update()
# TODO 2) build an inverted dict: {score: name for name, score in ...}
Sample: merged โ {'Ava': 88, 'Liam': 95, 'Noah': 79}, inverted โ {88: 'Ava', 95: 'Liam', 79: 'Noah'}
PCEP: 3.3 | AP CSP: AAP-3.B
Activity: Student Contact Bookยถ
Build a program that stores a contact book as a list of dictionaries. Each contact has a name, phone number, and email. The user can:
Add a new contact
Search by name
Delete a contact
Display all contacts
Use a while loop for the menu and a for loop to search/display.
๐งฉ Scaffolding โ build it in this order:
Hard-code one contact into the list and get โDisplay allโ working first.
Add โAdd a new contactโ (append to the list).
Add โSearch by nameโ (loop +
if).Add โDelete a contactโ last โ itโs the trickiest, since you must find the right item before removing it.
Starter code:
# Assignment: Program: Student Contact Book
# Description: Menu-driven contact book using a list of dictionaries.
# Language: Python 3.x
contacts = []
while True:
print("\n1) Add 2) Search 3) Delete 4) Display All 5) Quit")
choice = input("Choose an option: ")
if choice == "1":
# TODO: build a dict from user input, append to contacts
pass
elif choice == "2":
# TODO: ask for a name, loop through contacts, print matches
pass
elif choice == "3":
# TODO: ask for a name, find and remove the matching contact
pass
elif choice == "4":
# TODO: print every contact
pass
elif choice == "5":
break
else:
print("Invalid option.")
AP CSP: AAP-3.B, AAP-2.E | PCEP: 3.3
Mixed Review โ Quick Check (all four types)ยถ
A 4-question exit ticket for wrapping up the section โ one short question per data type, answerable in a sentence or two, no coding required.
String: You have
name = " DANA ". What doesname.strip().lower()return?List: Whatโs the difference between
.remove("apple")and.pop(0)on a list?Tuple: Why would you choose a tuple over a list to store a birthdate?
Dictionary: What error would
student["gpa"]raise if"gpa"isnโt a key โ and what method avoids that crash?
(Answer key: pull straight from the corresponding Thinking Questions above for Strings/Lists/Tuples/Dictionaries.)
Section 3 โ Standards Alignment Reference
Code |
Standard |
Where it shows up in this section |
|---|---|---|
|
Use data abstractions to manage complexity |
Section intro, Lists, Tuples (T4) |
|
Explain how data can be represented using bits |
Strings, Drills S1โS7, Receipt Formatter |
|
Use abstractions to organize data |
Dictionaries, Drills D1โD6, Student Contact Book |
|
Collect and process data using lists |
Lists, Drills L1โL7 |
|
Collect and process data using tuples |
Tuples, Drills T1โT5 |
|
Collect and process data using dictionaries |
Dictionaries, Drills D1โD6, Student Contact Book |
|
Operate with strings |
Strings, Drills S1โS7, Receipt Formatter |
Section 4 โ Functions and Exceptionsยถ
๐ AP CSP: AAP-3.B โ Use procedures/functions to manage complexity.
Functions, Methods, and Proceduresยถ
๐ PCEP 4.1 โ Decompose the code using functions
๐ AP CSP: AAP-3.B โ Abstractions; CRD-2.G โ Call procedures.
Term |
Python Form |
Returns Value? |
Used For |
|---|---|---|---|
Function |
|
โ Yes |
Computing and returning a result |
Procedure |
|
โ No |
Side effects (printing, updating) |
Method |
Function inside a class |
โ /โ |
Behaviors belonging to an object |
# Function โ returns a value
def add(a, b):
return a + b
result = add(3, 5) # result = 8
# Procedure โ no return value
def greet(name):
print(f"Hello, {name}!")
greet("Alice") # Hello, Alice!
Parameters vs. Arguments
# 'a' and 'b' are PARAMETERS (in definition)
def multiply(a, b):
return a * b
# 5 and 3 are ARGUMENTS (passed at call)
multiply(5, 3)
๐ Worked Example โ functions in a different context (rectangle area)
def calculate_area(length, width): return length * width def main(): l = float(input("Enter length: ")) w = float(input("Enter width: ")) area = calculate_area(l, w) print(f"Area = {area}") main()This shows the shape youโll reuse five times below: a function that takes parameters and
returns a result (noprint()inside it), called frommain(), with the result stored in a variable and printed with an f-string. Get this pattern solid here before writingget_sum(),get_difference(), and the rest.
Activity: Math Operations Calculatorยถ
Write a Python program called math_operations.py that performs addition, subtraction, multiplication, division, and modulo on two numbers entered by the user โ using a separate function for each operation.
๐งฉ Scaffolding โ build it in this order:
Write
get_sum()and call it frommain()โ confirm it prints correctly.Copy that pattern to write
get_difference(),get_product(),get_modulo().Write
get_quotient()last โ it needs the extra care described in Requirement 4 below.Only after everything works, add the stretch-challenge
try/exceptfor divide-by-zero.
Requirements:
Write five functions:
get_sum(num1, num2),get_difference(num1, num2),get_product(num1, num2),get_quotient(num1, num2), andget_modulo(num1, num2). Each function should perform one calculation andreturnthe result (noprint()inside the functions).In
main(), prompt the user for two numbers usinginput(), and convert them to the correct type (intfor whole numbers).Call each function, store the result in a variable, and print all five results using f-strings, formatted like:
Sum = 15 Difference = 5 Product = 50 Quotient = 2.00 Remainder = 0
get_quotient()should always return afloatโ usefloat()or a division with/(not//) to avoid integer division.Include a full program header block (Assignment, Author, Description, etc.) at the top, following the course style guide.
Stretch challenge: What happens if the user enters 0 as the second number for division or modulo? Add a try/except block (or an if check) so the program doesnโt crash โ print a friendly error message instead.
Teaching note: This activity mirrors a classic C-language exercise (functions returning
int/float, separate function per operation) โ a good one to point out explicitly if any students are also in a C/C++ or Java course, since the logic is identical even though the syntax (no semicolons, no type declarations, no#include) is different.
PCEP: 1.4, 1.5, 4.1 | AP CSP: AAP-2.F, AAP-3.B
Default Parameter Values
def greet(name, message="Hello"):
print(f"{message}, {name}!")
greet("Alice") # Hello, Alice!
greet("Bob", "Good morning") # Good morning, Bob!
Positional, Keyword, and Mixed Arguments
def describe(name, age, city):
print(f"{name}, {age}, from {city}")
describe("Alice", 16, "HB") # Positional
describe(age=16, name="Alice", city="HB") # Keyword
describe("Alice", city="HB", age=16) # Mixed
The return Keyword and None
def square(n):
return n ** 2
result = square(4) # result = 16
def do_nothing():
pass
print(do_nothing()) # None โ functions without return give None
Variable Scope and global
counter = 0 # global variable
def increment():
global counter # must declare to modify global
counter += 1
increment()
print(counter) # 1
Name Hiding (Shadowing): A local variable with the same name as a global hides (shadows) the global inside the function.
x = 10
def show():
x = 99 # local x shadows global x
print(x)
show() # 99
print(x) # 10 โ global unchanged
๐ค Thinking Question โ check your answer
Question: Why does the course style guide (Guideline #11 above) tell you to avoid global variables and use function parameters/return values instead?
Answer: Functions that rely on global are harder to test and reason about in isolation โ you have to know the current state of a variable defined somewhere else in the file to predict what the function will do, and any function can silently change that shared state. A function that takes parameters and returns a value is self-contained: give it the same inputs, and you always get the same output, regardless of what else is happening in the program. This โno hidden side effectsโ property is exactly what the CPTโs Prompt 3c is asking you to explain about your own function.
Recursion
A recursive function calls itself. Every recursion needs a base case to stop.
def factorial(n):
if n == 0: # base case
return 1
return n * factorial(n - 1) # recursive case
print(factorial(5)) # 120
Call stack visualization:
factorial(5)
factorial(4)
factorial(3)
factorial(2)
factorial(1)
factorial(0) โ base case: returns 1
๐ค Thinking Question โ check your answer
Question: What happens if you write a recursive function and forget the base case entirely?
Answer: The function calls itself forever (or until Python hits its recursion limit and raises a RecursionError), because thereโs never a condition that tells it to stop and start returning values back up the call stack. Every recursive function you write should let you point to the exact line that is the base case, and explain why the recursive case is guaranteed to reach it eventually.
Activity: Recursion Practice โ Three Levelsยถ
Recursion clicks once you can identify two things in any recursive problem: the base case (when to stop) and the recursive case (how the problem gets smaller each call). Practice with three functions, in order:
Level 1 โ Countdown. Write countdown(n) that prints every number from n down to 1, then prints "Liftoff!" โ using recursion, not a loop. Identify: what is the base case? What gets smaller with each call?
Level 2 โ Sum of a list. Write list_sum(numbers) that returns the sum of all numbers in a list using recursion (hint: the base case is an empty list, which sums to 0; the recursive case is numbers[0] + list_sum(numbers[1:])).
Level 3 โ Power function. Write power(base, exponent) that calculates base ** exponent using recursion instead of the ** operator. (Base case: exponent == 0 returns 1.)
For each function, before coding, write on paper:
What is the base case, and what does it return?
What is the recursive case, and how does it move the problem toward the base case?
Trace through one full call by hand (like the
factorial(5)call stack above) and predict the output.
Stretch challenge: Add a print() inside each function showing the current callโs argument and indenting one extra level per recursive call โ this visually recreates the call stack diagram above, using your own code.
PCEP: 4.1
Modules and Packagesยถ
๐ AP CSP: AAP-2.G โ Use abstraction to manage complexity.
A module is a .py file containing reusable code. A package is a directory of related modules.
import math
import random
import datetime
print(math.sqrt(16)) # 4.0
print(random.randint(1, 10)) # random int 1โ10
print(datetime.date.today()) # today's date
Import Styles
import math # access as math.sqrt()
from math import sqrt # access as sqrt()
from math import sqrt, pi # import multiple
import numpy as np # alias
Common Standard Library Modules
Module |
Purpose |
Example |
|---|---|---|
|
Math functions |
|
|
Random numbers |
|
|
Date/time |
|
|
Operating system |
|
|
JSON data |
|
|
GUI windows |
|
๐ค Thinking Question โ check your answer
Question: Whatโs the practical difference between import random and from random import randint, in terms of how youโd call the function afterward?
Answer: With import random, you must prefix every call with the module name: random.randint(1, 10). With from random import randint, you import just that one function directly into your fileโs namespace, so you call it as randint(1, 10) with no prefix. The first style is safer in larger programs (itโs always clear which module a function came from); the second is more convenient for one or two functions youโll use constantly.
Project: Random Trivia Quiz Generatorยถ
Build a trivia quiz program that pulls together modules, functions, lists, dictionaries, and string formatting โ everything youโve learned so far โ into one working project.
๐งฉ Scaffolding โ build it across several days:
Day 1: Hard-code your 8 questions as a list of dictionaries; write
ask_question()and test it on ONE question.Day 2: Write
run_quiz()to loop through all questions and track score; get the raw score printing correctly.Day 3: Add
random(shuffle questions),datetime(timing), and the percentage-formatted score. Add the three import styles last.Day 4 (stretch): Add the
jsonsave/load high-score feature.
Requirements:
Use the
randommodule to randomly select and shuffle quiz questions each time the program runs.Use the
datetimemodule to timestamp the start and end of the quiz, and calculate/print how long the student took usingdatetime.datetime.now().Store at least 8 trivia questions as a list of dictionaries, each with keys like
"question","choices", and"answer".Write a function
ask_question(question_dict)that displays a question and its choices, collects the userโs answer, and returnsTrue/Falsefor correct/incorrect.Write a function
run_quiz(questions)that loops through all questions, tracks the score, and callsask_question()for each one.At the end, print the score as both a raw count (
"7/8 correct") and a percentage, formatted to 1 decimal place using an f-string.Import at least one module using each of the three import styles covered above (
import x,from x import y,import x as y).Include a full program header block.
Rubric (25 points total)
Criteria |
Points |
|---|---|
|
4 |
|
4 |
Questions stored as a list of dictionaries with consistent keys |
4 |
|
4 |
|
4 |
Score printed as both count and percentage (f-string formatting) |
2 |
All three import styles used correctly somewhere in the program |
2 |
Program header block, PEP 8 style, and no crashes on normal input |
1 |
Stretch challenge: Add a fourth import โ json โ to save the studentโs score to a scores.json file after each run, and load/display their best score at the start of the next run.
AP CSP: AAP-2.G, CRD-2.B
Exception Handlingยถ
๐ PCEP 4.4 โ Basics of Python Exception Handling
๐ AP CSP: CRD-2.J โ Test and debug programs.
What Is an Exception?
An exception is a runtime error that interrupts normal program flow. Without handling, it crashes the program. With try-except, you can catch and respond to errors gracefully.
Basic Syntax
try:
# code that may raise an exception
except SomeException as e:
# runs if the exception occurs
else:
# runs if NO exception occurred
finally:
# ALWAYS runs โ cleanup code
๐ Worked Example โ try/except in a different context (library checkout days)
try: days = int(input("How many days do you want to borrow this book? ")) if days > 21: raise ValueError("Max borrow period is 21 days.") days_of_slack = 21 - days except ValueError as e: print(f"Invalid entry: {e}") else: print(f"Checked out. {days_of_slack} days of slack before the 21-day limit.") finally: print("Checkout attempt logged.")Run this in your head with
days = "abc", thendays = 30, thendays = 10โ three different paths through the sametry/except/else/finallyblock, just like the calculator youโre about to build will handle three different kinds of bad input.
Example: Safe Division
try:
result = 10 / int(input("Enter a divisor: "))
except ValueError:
print("That wasn't a number!")
except ZeroDivisionError:
print("Can't divide by zero!")
else:
print(f"Result: {result}")
finally:
print("Operation attempted.")
Common Exceptions
Exception |
When it occurs |
|---|---|
|
Dividing by zero |
|
Wrong value type, e.g., |
|
Wrong data type in operation |
|
List index out of range |
|
Dictionary key not found |
|
File doesnโt exist |
|
Variable not defined |
Rule of thumb: Order
exceptblocks from most specific to most general. Python checks them in order and runs the first match.
try:
x = int("abc")
except ValueError: # caught here โ most specific
print("Value error")
except Exception: # broader fallback
print("Some error")
Propagating Exceptions
def divide(a, b):
return a / b # may raise ZeroDivisionError
try:
result = divide(10, 0) # exception propagates up to here
except ZeroDivisionError:
print("Caught in caller!")
File Handling with Exceptions
try:
with open("data.txt", "r") as file:
content = file.read()
except FileNotFoundError:
print("File not found.")
except IOError:
print("Error reading file.")
๐ PCEP note: the full
BaseExceptionโExceptionhierarchy tree (PCEP 4.3, exam-level memorization) is covered in PCEP Certification Path โ Semester 2. For the CPT, knowing the common exceptions table above and how to orderexceptblocks is enough.
๐ค Thinking Question โ check your answer
Question: Why does a well-designed program almost always use try/except instead of just trusting that user input will be valid?
Answer: You canโt control what a user types โ they might enter letters where a number is expected, leave a field blank, or divide by zero by accident. try/except lets your program catch that bad input and respond gracefully (with a clear message) instead of crashing outright. For the CPT specifically, a program that crashes on unexpected input during your recorded video will hurt your score โ exception handling is part of writing a robust, testable program.
Activity: Safe Calculatorยถ
Build a calculator program that:
Accepts two numbers and an operator (
+,-,*,/)Handles
ZeroDivisionErrorandValueErrorLoops until the user types
quitUses a function for each operation
Uses
try-except-else-finally
๐งฉ Scaffolding โ build it in this order:
Write the four operation functions (reuse them from Math Operations Calculator if youโve already built that).
Get ONE calculation working with
try/exceptaround it, no loop yet.Wrap it in a
whileloop that exits on"quit".Add
else/finallylast.
Starter code:
# Assignment: Program: Safe Calculator
# Description: Menu-driven calculator with full exception handling.
# Language: Python 3.x
def add(a, b): return a + b
def subtract(a, b): return a - b
def multiply(a, b): return a * b
def divide(a, b): return a / b
while True:
op = input("Enter operator (+, -, *, /) or 'quit': ")
if op == "quit":
break
try:
num1 = float(input("First number: "))
num2 = float(input("Second number: "))
# TODO: call the right function based on `op`
# TODO: handle an unrecognized operator too
except ValueError:
print("Please enter valid numbers.")
except ZeroDivisionError:
print("Can't divide by zero.")
else:
# TODO: print the result
pass
finally:
print("Attempt complete.\n")
AP CSP: CRD-2.J | PCEP: 4.4
File Operationsยถ
๐ AP CSP: AAP-3.A, CRD-2.B, CRD-2.J โ Collect and represent data; implement and test programs.
Explanation
So far, every program youโve written loses all its data the moment it stops running. File operations let a program read data that already exists on disk and write data that survives after the program ends โ the difference between a program that โremembersโ and one that starts from zero every time.
Pythonโs open() function is the entry point for all file work. The safest way to use it is with a with block, which automatically closes the file for you โ even if an error happens partway through.
with open("filename.txt", "r") as file:
content = file.read()
# file is automatically closed here, even if something went wrong
Key Words
Term |
Meaning |
|---|---|
File mode |
How youโre opening the file: |
|
Automatically closes the file when the block ends โ always prefer this over manual |
Stream |
The connection Python keeps open to the file while youโre reading/writing it |
Newline character ( |
Marks the end of a line โ |
Text file |
Stores human-readable characters ( |
Delimiter |
The character that separates fields on a line (commonly |
File Modes
Mode |
Meaning |
If file doesnโt exist |
If file exists |
|---|---|---|---|
|
Read only |
Raises |
Reads from the start |
|
Write only |
Creates it |
Erases existing content first |
|
Append |
Creates it |
Adds to the end, keeps existing content |
|
Read and write |
Raises |
Reads and writes without erasing |
Common trap:
"w"mode erases the entire file the instant you open it โ even if you never write anything. If you need to keep existing content, use"a"(append) or"r+".
Reading Files โ Three Ways
with open("data.txt", "r") as file:
whole_thing = file.read() # one big string, includes \n characters
with open("data.txt", "r") as file:
all_lines = file.readlines() # list of strings, one per line (keeps \n)
with open("data.txt", "r") as file:
for line in file: # loop line-by-line โ best for large files
print(line.strip()) # .strip() removes the trailing \n
Writing and Appending
with open("output.txt", "w") as file:
file.write("First line\n")
file.write("Second line\n")
with open("output.txt", "a") as file:
file.write("This gets added to the end\n")
When Would You Use This?
Any time your program needs to outlive a single run โ saving a high score, loading a roster of students, or writing a log of every calculation a user made. File operations are also how real programs move data between each other: one program writes a file, another reads it.
Best Practices
Always use
with open(...) as file:โ never callopen()without it.Always wrap file access in
try/except, catchingFileNotFoundErrorat minimum.Double-check your mode before writing โ
"w"silently destroys existing content.Use
.strip()on every line you read from a text file to remove the trailing\nbefore you use the value.
๐ค Thinking Question โ check your answer
Question: You want to add a new high score to the end of an existing scores file without erasing the old scores. Which file mode do you use, and what would go wrong if you used "w" instead?
Answer: Use "a" (append). "w" mode erases the entire existing file the moment you open it, so every previous score would be gone before you ever wrote the new one โ youโd end up with a file containing only the newest score.
๐ Worked Example โ file loading in a different context (favorite colors)
def load_colors(filename): try: with open(filename, "r") as file: return [line.strip() for line in file] except FileNotFoundError: return [] colors = load_colors("colors.txt") print(f"Loaded {len(colors)} colors:", colors)This โtry to load, fall back to an empty listโ pattern is the backbone of the roster-loading function youโll write below โ same technique, different data.
Sample Program
# Append a new high score to a running log file, then print the full history
def log_score(name, score, filename="scores_log.txt"):
with open(filename, "a") as file:
file.write(f"{name},{score}\n")
def show_log(filename="scores_log.txt"):
try:
with open(filename, "r") as file:
for line in file:
name, score = line.strip().split(",")
print(f"{name}: {score}")
except FileNotFoundError:
print("No scores logged yet.")
log_score("Alice", 91)
log_score("Bob", 76)
show_log()
Activity: Student Roster File Managerยถ
Write a program that manages a roster stored in a text file called roster.txt (one name per line).
๐งฉ Scaffolding โ build it in this order:
Write
load_roster()alone and test it (both with and without an existingroster.txt).Write
add_student()and confirm names actually persist between separate runs of the program.Write
save_roster()and wire it into the โremoveโ menu option.Build the
main()menu loop last, once all three functions work independently.
Requirements:
Write a function
load_roster(filename)that reads the file and returns a list of names. If the file doesnโt exist, catchFileNotFoundErrorand return an empty list instead of crashing.Write a function
add_student(filename, name)that appends a new name to the file (donโt overwrite the existing roster!).Write a function
save_roster(filename, roster)that overwrites the file with the current contents of a roster list (one name per line) โ used after removing a student.In
main(), build a menu (whileloop) that lets the user: view the roster, add a student, remove a student (remove from the in-memory list, then callsave_roster()), or quit.Every write operation must use a
withblock. Every read must be wrapped in atry/except FileNotFoundError.
AP CSP: AAP-3.A, CRD-2.B, CRD-2.J
Actual Program with Test Samples
# Assignment: File Operations โ Student Roster File Manager
# Description: Loads, adds to, removes from, and saves a student roster stored in a text file.
# Language: Python 3.x
def load_roster(filename):
try:
with open(filename, "r") as file:
return [line.strip() for line in file if line.strip()]
except FileNotFoundError:
return []
def add_student(filename, name):
with open(filename, "a") as file:
file.write(name + "\n")
def save_roster(filename, roster):
with open(filename, "w") as file:
for name in roster:
file.write(name + "\n")
def main():
filename = "roster.txt"
roster = load_roster(filename)
while True:
print("\n1) View 2) Add 3) Remove 4) Quit")
choice = input("Choose an option: ")
if choice == "1":
print("Roster:", roster)
elif choice == "2":
name = input("Name to add: ")
add_student(filename, name)
roster.append(name)
elif choice == "3":
name = input("Name to remove: ")
if name in roster:
roster.remove(name)
save_roster(filename, roster)
else:
print(f"{name} not found.")
elif choice == "4":
break
else:
print("Invalid option.")
if __name__ == "__main__":
main()
Sample Test Cases
Starting |
Action |
Resulting Roster |
|---|---|---|
(file doesnโt exist) |
|
|
|
|
|
|
remove |
|
Data Structures Deep Diveยถ
๐ AP CSP: AAP-3.A, AAP-3.B, AAP-3.C
Pythonโs built-in data structures serve different organizational needs:
Structure |
Ordered |
Mutable |
Indexed |
Duplicates |
Use When |
|---|---|---|---|---|---|
List |
โ |
โ |
โ |
โ |
General-purpose ordered collection |
Tuple |
โ |
โ |
โ |
โ |
Fixed data (coords, config) |
Set |
โ |
โ |
โ |
โ |
Unique values, set operations |
Dictionary |
โ |
โ |
By key |
โ (keys) |
Key-value lookup |
Sets
user_ids = {101, 102, 103, 101} # duplicates removed
print(user_ids) # {101, 102, 103}
a = {1, 2, 3}
b = {2, 3, 4}
print(a | b) # Union: {1, 2, 3, 4}
print(a & b) # Intersection: {2, 3}
print(a - b) # Difference: {1}
๐ค Thinking Question โ check your answer
Question: A teacher wants to know which students are enrolled in both Period 1 and Period 3. Would a list or a set make that check easier to write, and why?
Answer: A set, because set intersection (&) does exactly that check in one operation: period_1 & period_3. With lists youโd have to manually loop through one list and check membership in the other, writing several lines of code to do what a set does in one.
Stacks (LIFO) using Lists
A stack follows LIFO โ Last In, First Out. Think of a stack of plates: you add a new plate to the top, and you also take from the top. The most recently added item is always the first one removed. Pythonโs undo button behavior, browser back-buttons, and function call stacks (like the recursion diagram above!) all work this way.
stack = []
stack.append("action1") # push โ add to the top
stack.append("action2")
last = stack.pop() # pop โ "action2" (last one in, first one out)
Queues (FIFO) using deque
A queue follows FIFO โ First In, First Out. Think of a line at a store checkout: the first person in line is the first person served. New items are added to the back and removed from the front.
from collections import deque
queue = deque()
queue.append("first") # add to the back
queue.append("second")
first = queue.popleft() # remove from the front โ "first" (first one in, first one out)
Memory trick: LIFO = last in, first out (a stack of trays). FIFO = first in, first out (a line at the store). Both use
.append()to add, but stacks remove with.pop()(from the end) while queues remove with.popleft()(from the front).
๐ PCEP note: frozensets โ an immutable version of
setโ are a PCEP-adjacent topic that build on this section. Theyโre covered in PCEP Certification Path โ Semester 2, along with a capstone project that ties frozensets together with file operations, built-in functions, and lambdas.
Object-Oriented Programming (OOP)ยถ
๐ AP CSP: AAP-3.B โ Abstractions; CRD-2.B โ Implement in a language.
Why OOP? Real programs model real-world things โ students, cars, bank accounts, game characters. OOP lets us group data and behavior together in reusable, organized units called classes.
Core OOP Conceptsยถ
Key Vocabulary
Term |
Definition |
|---|---|
Class |
A blueprint/template for creating objects |
Object |
An instance (specific example) of a class |
Attribute |
A variable that belongs to a class or object |
Method |
A function that belongs to a class |
Constructor |
|
|
Refers to the current instance of the class |
Encapsulation |
Bundling data and methods together |
Inheritance |
A child class acquires attributes/methods from a parent |
Polymorphism |
Different classes can share the same method name |
Building a Classยถ
class Car:
"""Blueprint for a car object."""
def __init__(self, make, model, year):
"""Constructor โ runs automatically when object is created."""
self.make = make # instance attribute
self.model = model # instance attribute
self.year = year # instance attribute
def start_engine(self):
"""Instance method โ uses self to access attributes."""
print(f"The {self.year} {self.make} {self.model}'s engine is running.")
def stop_engine(self):
print(f"The {self.year} {self.make} {self.model}'s engine is off.")
def __str__(self):
"""Magic method โ controls how object prints."""
return f"{self.year} {self.make} {self.model}"
Creating and Using Objects
car1 = Car("Toyota", "Corolla", 2022) # create object
car2 = Car("Honda", "Civic", 2020)
car1.start_engine() # The 2022 Toyota Corolla's engine is running.
print(car2) # 2020 Honda Civic (uses __str__)
# Access attributes directly
print(car1.make) # Toyota
car1.year = 2023 # Modify an attribute
๐ค Thinking Question โ check your answer
Question: What is self, really โ why does every instance method need it as the first parameter?
Answer: self is how a method knows which object itโs currently operating on. When you call car1.start_engine(), Python automatically passes car1 in as self, so self.make inside the method refers to car1โs make specifically โ not car2โs. Without self, a method would have no way to tell one objectโs data apart from anotherโs.
Types of Methodsยถ
class Dog:
species = "Canis familiaris" # CLASS attribute (shared by all instances)
def __init__(self, name, breed=None):
self.name = name # INSTANCE attribute (unique per object)
self.breed = breed
def bark(self): # INSTANCE method
print(f"{self.name} says Woof!")
@classmethod
def get_species(cls): # CLASS method โ operates on the class
return cls.species
@staticmethod
def is_domestic(): # STATIC method โ no instance or class needed
return True
Inheritanceยถ
Inheritance allows a child class to reuse and extend the behavior of a parent class.
class Animal:
def __init__(self, name):
self.name = name
def speak(self):
return "..."
class Dog(Animal): # Dog inherits from Animal
def speak(self): # Override the parent method
return f"{self.name} says Woof!"
class Cat(Animal):
def speak(self):
return f"{self.name} says Meow!"
animals = [Dog("Rex"), Cat("Whiskers"), Dog("Buddy")]
for animal in animals:
print(animal.speak()) # Polymorphism in action
๐ค Thinking Question โ check your answer
Question: In the animals loop above, every object calls .speak() the same way, but Dog objects and Cat objects print different things. Whatโs this called, and why is it useful?
Answer: This is polymorphism โ different classes responding to the same method call in their own way. Itโs useful because the loop doesnโt need to know or care whether each item is a Dog or a Cat; it just calls .speak() and trusts each object to know how to respond correctly. This lets you add a Bird class later without ever touching the loop.
Magic / Dunder Methodsยถ
class BankAccount:
def __init__(self, holder, balance=0):
self.holder = holder
self.balance = balance
def __str__(self):
return f"Account({self.holder}, ${self.balance:.2f})"
def __len__(self):
return self.balance
def deposit(self, amount):
if amount > 0:
self.balance += amount
print(f"Deposited ${amount:.2f}. New balance: ${self.balance:.2f}")
def withdraw(self, amount):
if 0 < amount <= self.balance:
self.balance -= amount
print(f"Withdrew ${amount:.2f}. New balance: ${self.balance:.2f}")
else:
print("Insufficient funds or invalid amount.")
def get_balance(self):
return self.balance
# Usage
account = BankAccount("Alice Smith", 1000)
print(account) # Account(Alice Smith, $1000.00)
account.deposit(500)
account.withdraw(200)
account.withdraw(1500) # Insufficient funds
๐ Worked Example โ inheritance in a different context (vehicles)
class Vehicle: def __init__(self, brand, model, year): self.brand = brand self.model = model self.year = year def describe(self): return f"{self.year} {self.brand} {self.model}" class Motorcycle(Vehicle): def sound(self): return f"The {self.model} roars to life." bike = Motorcycle("Harley-Davidson", "Iron 883", 2023) print(bike.describe()) print(bike.sound())This is the minimum skeleton โ one base class, one subclass, one object, with the subclass overriding/adding a method. Whichever OOP Zoo option you choose below (Zoo, Library, or RPG Party), youโll build that same base-class โ subclass โ container-class shape, just in your own domain.
Activity: OOP Zooยถ
Design a Zoo simulation using OOP:
Create a base class
Animalwith attributesname,species,age, and a methoddescribe().Create at least three subclasses (e.g.,
Mammal,Bird,Reptile) that each override aspeak()method.Create a
Zooclass that holds a list ofAnimalobjects and can:add_animal(animal)show_all()โ print all animalsfind_by_species(species)โ return a list of matching animals
Create at least 5 animal objects, add them to the zoo, and demonstrate all methods.
Include __str__ and proper docstrings.
๐งฉ Scaffolding โ build it in this order:
Build
Animal+ one subclass + one object (see Worked Example above); confirm.describe()and.speak()both work.Add the remaining subclasses.
Build
Zoowith justadd_animal()andshow_all(); test with 2โ3 animals.Add
find_by_species()last.
Option 2 โ Library Management System
Design a simplified library check-out system using OOP:
Create a base class
Itemwith attributestitle,id_number,checked_out(defaultFalse), and a methoddescribe().Create at least two subclasses (e.g.,
Book,Magazine) that each overridedescribe()to include a subclass-specific detail (author forBook, issue number forMagazine).Create a
Libraryclass that holds a list ofItemobjects and can:add_item(item)check_out(id_number)โ marks an item unavailable, or prints a message if already checked outreturn_item(id_number)โ marks an item available againlist_available()โ prints all items currently not checked out
Create at least 5 items, add them to the library, and demonstrate checking items in and out.
Option 3 โ RPG Character Builder
Design a role-playing game character system using OOP:
Create a base class
Characterwith attributesname,health,level, and a methodattack()that returns a damage value.Create at least three subclasses (e.g.,
Warrior,Mage,Healer) that each overrideattack()with different behavior (e.g.,Magedeals more damage but has less health;Healerโsattack()restores health to an ally instead).Create a
Partyclass that holds a list ofCharacterobjects and can:add_member(character)show_party()โ print all members and their statstotal_party_health()โ return the combined health of all members
Create at least 4 characters across different subclasses, add them to a party, and simulate a round of attacks.
Rubric for All Three Options (10 points)
Criteria |
Points |
|---|---|
Base class correctly defined with required attributes and at least one method |
2 |
At least the required number of subclasses created, each correctly inheriting from the base class |
2 |
Each subclass overrides the specified method with genuinely different behavior (not just a copy/paste) |
2 |
Container class ( |
2 |
Program includes |
2 |
AP CSP: AAP-3.B, CRD-2.B | Demonstrates: Abstraction, Inheritance, Polymorphism
AP CSP Performance Task Preparationยถ
๐ AP CSP: CRD-2 โ Program Design and Development
The AP CSP Create Performance Task (CPT) is 30% of your AP score. You write a program and submit written responses explaining it. Everything in Sections 1โ4, File Operations, Data Structures, and OOP above was chosen and sequenced specifically to get you ready for this. This section ties it all together.
CPT Requirements at a Glanceยถ
Requirement |
Description |
|---|---|
Program Purpose |
Clearly state what problem your program solves |
Algorithm |
Must include sequencing, selection, AND iteration |
Abstraction |
Must use a list (or other collection) and a procedure/function |
Procedure with parameter |
A function that takes input and affects behavior |
Output |
Must produce visible output based on input |
CPT Checklist โ Does Your Program Have?ยถ
โ A clear purpose that solves a meaningful problem
โ Input from the user or a data source
โ Output that responds to the input
โ An algorithm using:
โ Sequencing (steps in order)
โ Selection (
if-elif-else)โ Iteration (
fororwhileloop)
โ A list (or collection) that stores and processes data
โ At least one student-defined function that:
โ Has a parameter that affects behavior
โ Is called at least once in the program
โ Your function implements an algorithm (not just print)
โ A second call to the function with different arguments
๐ Worked Example โ a minimal (but complete) CPT-checklist-passing program, annotated
def calculate_discount(price, is_member): # โ function WITH a parameter if is_member: # โ selection return price * 0.8 return price prices = [12.99, 45.00, 8.50, 30.00] # โ list (abstraction/collection) member_status = [True, False, True, False] total = 0 for i in range(len(prices)): # โ iteration final_price = calculate_discount(prices[i], member_status[i]) # โ function CALLED print(f"Item {i+1}: ${final_price:.2f}") total += final_price print(f"Total: ${total:.2f}") # โ output print(calculate_discount(20.00, True)) # โ SECOND call, different argumentsChecklist walk-through: purpose (calculate discounted checkout totals) โ , input (the
prices/member_statuslists stand in for real input โ a live CPT would useinput()or a data file) โ , sequencing/selection/iteration โ (theforloop +if), abstraction (thepriceslist) โ , function with a parameter thatโs called twice with different arguments โ , visible output โ . This is intentionally small โ your actual CPT program should be a full, original idea, but every one of these six boxes has to be checkable in it exactly like this.
CPT Written Response Tipsยถ
Prompt 3a โ Program Function and Purpose:
Describe what your program does and the problem it solves
Describe what input your program accepts
Describe the output your program produces
Prompt 3b โ Data Abstraction:
Show your code that stores data in a list (or other collection)
Explain what data is in the list and how it represents information
Explain what would be harder without using a list
Prompt 3c โ Managing Complexity:
Show your function/procedure with a parameter
Explain what the parameter does and how it affects output
Explain how the function manages complexity
Prompt 3d โ Procedural Abstraction:
Identify the algorithm embedded in your function
Describe the algorithm step-by-step in plain English
Activity: CPT Brainstorming Workshopยถ
With a partner, evaluate 3 program ideas against the CPT checklist. For each idea, identify:
The input source
The list and what it stores
The function and its parameter
The algorithm (sequencing, selection, iteration)
Select the strongest idea and create a one-page project proposal.
AP CSP: CRD-2.A, CRD-2.B
Practice PT 1 โ Idea Listยถ
Use these for the Practice PT 1 window (Weeks 8โ11). Each idea below already satisfies the CPT checklist โ the point of Practice PT 1 is rehearsing the process (design โ code โ test โ written responses) on a smaller, guided idea before the pressure of the real thing. Pick ONE, or propose your own and run it past your teacher.
# |
Idea |
Input |
List/Collection |
Function + Parameter |
Output |
|---|---|---|---|---|---|
1 |
Grade Book Averager |
Scores typed in one at a time |
List of scores |
|
Class average + list of letter grades |
2 |
Password Strength Checker |
A password string |
List of the passwordโs characters |
|
Strength rating + specific feedback |
3 |
Tip Calculator for a Group |
Bill amounts for each person |
List of bill amounts |
|
Per-person tip + group total |
4 |
Word Frequency Counter |
A sentence or short paragraph |
List of words (via |
|
Most frequent word + its count |
5 |
Simple To-Do List Manager |
Task names typed in a loop |
List of tasks |
|
Printed to-do list with statuses |
๐งฉ Scaffolding for Practice PT 1 (Weeks 8โ11):
Week 8 (intro): Choose your idea; sketch input/output on paper; identify your list and your function-with-parameter before writing any code.
Week 9: Write the function alone and test it with at least 3 different inputs, by itself, before wiring it into the full program.
Week 10: Build the full program around the function โ the loop, the list, the two function calls with different arguments.
Week 11 (due Fri): Test edge cases (empty input, unexpected input), then write your four written responses (3aโ3d) using the CPT Written Response Tips above.
Practice PT 2 โ Idea Listยถ
Use these for the Practice PT 2 window (Weeks 12โ14). These are intentionally a step up from Practice PT 1 โ most naturally pull in a dictionary or list-of-dictionaries, File Operations, or OOP (all covered by Week 12), which the official December CPT will likely also benefit from.
# |
Idea |
Input |
List/Collection |
Function + Parameter |
Output |
|---|---|---|---|---|---|
1 |
Student Roster Analyzer |
Names + scores, one student at a time |
List of dictionaries ( |
|
Class roster with pass/fail flags + class average |
2 |
Inventory Restock Alert |
Item names + current stock counts |
List of dictionaries ( |
|
List of items that need restocking |
3 |
Simple Save/Load High Score Game |
A guessed number, repeated |
List of past guesses (this run) |
|
Feedback per guess + score saved to file with File Operations |
4 |
Basic Animal Shelter Tracker (OOP) |
Animal name/species/age, one at a time |
List of |
An |
Roster of animals + count by species |
5 |
Movie Recommendation Filter |
A list of movies with genre/rating |
List of dictionaries |
|
Filtered list of recommended movies |
๐งฉ Scaffolding for Practice PT 2 (Weeks 12โ14):
Week 12 (intro): Choose your idea; if it uses File Operations, get a working
load()/save()pair before building the rest.Week 13: Build the core loop and function; if itโs the OOP option, get your class working standalone first (like the OOP Zoo Worked Example above).
Week 14 (due Fri): CPT-checklist self-review workshop in class, then submit code + written responses.
Final Project Optionsยถ
Option A โ Text-Based Adventure Game (BeginnerโIntermediate)ยถ
Build a multi-room text adventure with:
Classes for
Player,Room, andItemA
listof rooms and itemsA
whileloop game engineUser-input navigation using
if-elifAt least one function with a parameter
Exception handling for invalid input
A scoring system
AP CSP Alignment: CRD-2.B, AAP-2.E, AAP-3.B, AAP-4.A
Option B โ Student Data Tracker (Intermediate)ยถ
Build a command-line tracker that:
Stores student records as a list of dictionaries
Supports add, delete, search, and update operations
Calculates class average, highest/lowest scores
Uses OOP (a
Studentclass with methods)Handles all exceptions gracefully
Optionally reads/writes to a
.jsonfile
AP CSP Alignment: CRD-2.B, AAP-3.A, AAP-3.B, AAP-3.C, AAP-4.A
Option C โ Mini Minesweeper (Advanced)ยถ
Build a simplified Minesweeper game using Python and Tkinter with OOP design.
Project Objectives
A 6ร6 grid stored as a 2D list
Random mine placement
Count adjacent mines per cell
Allow player left-clicks and right-click flagging
Score tracking for safe clicks
Move history stored in a list
Game over on mine click
Flood-fill reveal using recursion
Win/loss detection
โTry Againโ button to reset
AP CSP Alignment: AAP-2.E, AAP-4.A, CRD-2.B, CRD-2.J
AP CSP Learning Goals:
AAP-2.Eโ Algorithms with sequencing, selection, iterationAAP-4.Aโ Data abstractions (lists/2D lists) to manage complexityCRD-2.Bโ Implementing algorithms in a programming languageCRD-2.Jโ Testing and debugging
Starter Code Skeleton:
import tkinter as tk
import random
class Minesweeper:
def __init__(self, root):
self.root = root
self.root.title("<<YOUR NAME>> Minesweeper")
self.size = 6
self.mines = 6
# TODO: Create instance variables for:
# self.score, self.high_score, self.moves (list), self.flags (set)
self.grid_frame = tk.Frame(root)
self.grid_frame.pack()
self.create_board()
def create_board(self):
self.buttons = []
for r in range(self.size):
row = []
for c in range(self.size):
btn = tk.Button(self.grid_frame, width=3, height=1)
btn.grid(row=r, column=c)
btn.bind("<Button-1>", lambda e, r=r, c=c: self.click(r, c))
btn.bind("<Button-3>", lambda e, r=r, c=c: self.flag(r, c))
row.append(btn)
self.buttons.append(row)
self.place_mines()
def place_mines(self):
self.mine_locations = set()
# TODO: Randomly place self.mines mines in mine_locations
def count_mines(self, r, c):
count = 0
# TODO: Check 8 surrounding cells; count mines
return count
def click(self, r, c):
# TODO: Prevent clicking flagged or already-revealed cells
# TODO: If mine โ game_over(); else reveal number
# TODO: If count == 0 โ reveal_empty(r, c)
# TODO: Update score and moves list
pass
def reveal_empty(self, r, c):
# TODO: Recursive flood-fill to reveal adjacent empty cells
pass
def flag(self, r, c):
# TODO: Toggle flag on/off; limit flags to self.mines count
pass
def game_over(self):
# TODO: Reveal all mines; disable all buttons
pass
root = tk.Tk()
game = Minesweeper(root)
root.mainloop()
Extensions (Choose Any):
Flagging system with right-click ๐ฉ
High score tracking
Timer scoring
Difficulty levels: Easy (6ร6), Medium (8ร8), Hard (10ร10)
Win screen
Save/load high score to file
Grading Rubric (10 Points)
Criteria |
Points |
|---|---|
Mine placement algorithm works correctly |
1 |
Nearby mine counting algorithm works |
2 |
Flagging system implemented and limited |
2 |
Empty region reveal (recursion) |
2 |
Lists/sets used for tracking game data |
1 |
Program runs correctly; tested and debugged |
1 |
Reflection (text file) |
1 |
Total |
10 |
Reflection (1 page): Explain your mine-counting algorithm, how recursion works in the reveal function, and how lists are used for data abstraction.
PCEP Certification Path โ Semester 2ยถ
๐
Semester 2, Jan โ June
What this section is: everything thatโs specifically on the PCEP-30-02 exam but wasnโt needed for the AP CSP CPT โ pulled out of Semester 1 so it doesnโt compete with CPT prep, and spread across Semester 2 at a pace that gives it room to actually stick. You already have a full year of general Python fluency (Sections 1โ4, File Operations, OOP) under your belt by the time you start this section โ that foundation is what makes this content approachable now instead of overwhelming back in the fall.
๐ PCEP Pacing Guide โ Semester 2 (Tentative)ยถ
Jan 5 โ Jun (exam window TBD by district testing calendar)
Weeks |
Content Focus |
Activities |
|---|---|---|
1โ3 |
Numeral Systems; Bitwise Operators |
Bitwise โTry It Yourselfโ trace practice |
4โ6 |
Built-In Functions ( |
Grade Report Refactor |
7โ8 |
Lambda Functions |
Sort It Your Way |
9โ10 |
Frozensets |
Class Roster Overlap |
11 |
Exception Hierarchy Deep Dive |
Hierarchy trace practice |
12โ13 |
Culminating Project: Library Catalog Analyzer |
โ |
14+ |
PCEP practice exams; targeted review by exam section |
โ |
(Remaining CSP Big Ideas not covered by the CPT continue in parallel per the departmentโs existing JanuaryโApril sequence โ see [[cs-pathway-planning]].)
Numeral Systemsยถ
๐ PCEP 1.3 โ Introduce numeral systems
Python supports writing integer literals in multiple bases:
decimal = 255 # base 10
binary = 0b11111111 # base 2 โ 255
octal = 0o377 # base 8 โ 255
hexadecimal = 0xFF # base 16 โ 255
print(binary, octal, hexadecimal) # All print: 255 255 255
๐ค Thinking Question โ check your answer
Question: 0b1010 โ what decimal value does this represent, and how do you get there?
Answer: 10. Reading the binary digits right to left, each position is worth a power of 2: 1010 = (1ร8) + (0ร4) + (1ร2) + (0ร1) = 8 + 0 + 2 + 0 = 10.
Bitwise Operatorsยถ
๐ PCEP 1.4
Why Bitwise Operators Matter
Bitwise operators work directly on the binary representation of numbers โ the individual 0s and 1s โ rather than on the numberโs value as a whole. Most everyday Python code never needs them, but they matter for a few reasons worth knowing:
Theyโre on the PCEP exam โ youโre expected to trace what
&,|,^,~,<<, and>>do to binary values.They connect back to how computers actually store data โ ties directly to the binary/decimal/hex conversions you just learned.
They show up in real systems programming: setting individual permission flags (read/write/execute), working with network protocols, compressing data, and low-level graphics/hardware code all lean on bitwise operations because theyโre extremely fast and memory-efficient.
Shifts are a fast way to multiply/divide by powers of 2:
a << 1doubles a number,a >> 1halves it (integer division) โ a trick youโll sometimes see in performance-sensitive code.
Python numbers can be written in binary (base 2) using the 0b prefix. Each digit (called a bit) is worth a power of 2, reading right to left: 1, 2, 4, 8, 16โฆ
a = 0b1010 # decimal 10
b = 0b1100 # decimal 12
a = 1 0 1 0 โ 8+0+2+0 = 10
b = 1 1 0 0 โ 8+4+0+0 = 12
Bitwise operators compare two numbers column by column, one bit at a time.
AND (&) โ โboth must be 1โ
print(a & b) # 8
1010
& 1100
------
1000 โ 8
OR (|) โ โat least one must be 1โ
print(a | b) # 14
1010
| 1100
------
1110 โ 14
XOR (^) โ โexactly one, not bothโ
print(a ^ b) # 6
1010
^ 1100
------
0110 โ 6
NOT (~) โ flips every bit
~a = -(a + 1)
print(~a) # -11
Left shift (<<) โ slide bits left, fill with 0s
print(a << 1) # 20
1010 โ 10100
Each left shift by 1 is the same as multiplying by 2.
Right shift (>>) โ slide bits right, drop the end
print(a >> 1) # 5
1010 โ 101
Each right shift by 1 is the same as integer division by 2 (rounding down).
๐ค Thinking Question โ check your answer
Question: A student meant to write if is_valid and has_permission: but accidentally typed if is_valid & has_permission:. Will this usually still work? Why is it still bad practice?
Answer: With plain True/False booleans it will often appear to work, because & on booleans happens to behave like and in simple cases. But & is a bitwise operator, not a logical one โ it doesnโt short-circuit the way and does, and it behaves completely differently on non-boolean values (like actual integers). Mixing them up is a classic, hard-to-spot bug; always use and/or/not for logic and reserve &/|/^/~ for actual bit manipulation.
Try it yourself
Before running the code, convert a and b to binary on paper and work out each operation by hand column by column. Then check your answers with print().
a = 0b1010 # 10
b = 0b1100 # 12
print(a & b) # AND โ 8 (0b1000)
print(a | b) # OR โ 14 (0b1110)
print(a ^ b) # XOR โ 6 (0b0110)
print(~a) # NOT โ -11
print(a << 1) # Left shift โ 20
print(a >> 1) # Right shift โ 5
Built-In Functionsยถ
๐ AP CSP: AAP-2.G, AAP-3.B โ Use existing abstractions to manage complexity.
๐ PCEP-adjacent โ not a separately numbered PCEP-30-02 objective, but shows up throughout the certification examโs code-reading questions.
Explanation
A built-in function is a function Python provides for you automatically โ no import, no def, itโs just there the moment Python starts. Youโve already been using several (print(), len(), int(), range()) without thinking of them as a category. This section introduces the built-ins that let you transform, filter, and summarize data in a single line, instead of writing a for loop every time.
When Would You Use This?
Any time you need to apply the same operation to every item in a collection (map), keep only the items that meet a condition (filter), combine every item into one result (reduce), pair up two lists (zip), or ask a yes/no question about an entire collection (any, all) โ reach for a built-in before you reach for a loop.
Key Built-In Functions
Function |
What It Does |
Example |
Result |
|---|---|---|---|
|
Applies |
|
|
|
Keeps items where |
|
|
|
Combines all items into one value (needs |
|
|
|
Pairs up items from two+ iterables |
|
|
|
|
|
|
|
|
|
|
|
Returns a new sorted list |
|
|
|
Converts a Unicode code point to a character |
|
|
|
Converts a character to its Unicode code point |
|
|
Exam-style trap:
map()andfilter()return a lazy iterator, not a list โ you must wrap them inlist(...)to see or print the actual values.sorted()andmap()/filter()never modify the original list; they hand you back a brand-new one.
Example
scores = [55, 82, 91, 40, 76, 88]
passing = list(filter(lambda s: s >= 60, scores)) # keep passing scores
curved = list(map(lambda s: s + 5, scores)) # add 5 to every score
top_3 = sorted(scores, reverse=True)[:3] # highest 3 scores
print("Passing:", passing) # [82, 91, 76, 88]
print("Curved:", curved) # [60, 87, 96, 45, 81, 93]
print("Top 3:", top_3) # [91, 88, 82]
๐ค Thinking Question โ check your answer
Question: evens = filter(lambda x: x % 2 == 0, range(10)). You immediately try print(evens) and it does NOT show [0, 2, 4, 6, 8]. What went wrong, and how do you fix it?
Answer: filter() returns a lazy filter object, not a list โ printing it directly shows something like <filter object at 0x...>. You need to wrap it: print(list(evens)). This is a classic PCEP exam trap that applies to map() too.
Best Practices
Wrap
map()/filter()inlist()before printing โ otherwise youโll just see<filter object at 0x...>.If the built-in version is harder to read than a plain
forloop, use theforloop.sorted(iterable, key=func)is the built-in most worth mastering.reduce()needs an explicit import (from functools import reduce).
Activity: Grade Report Refactorยถ
Take the Grade Calculator activity you built earlier (Section 2) and refactor part of it using built-in functions instead of a for loop.
Requirements:
Start with a list of at least 10 student scores (
intvalues 0โ100).Use
filter()to build a list of students who passed (score โฅ 70).Use
map()to build a list of the same scores converted to a 4.0 GPA scale (formula:score / 25, rounded to 2 decimal places withround()).Use
sorted()with akey=andreverse=Trueto print the scores from highest to lowest without changing the original list.Use
any()to check whether any student scored a perfect 100, andall()to check whether every student passed.Print all five results with clear labels.
AP CSP: AAP-2.G, CRD-2.B
Actual Program with Test Samples
# Assignment: Built-In Functions โ Grade Report Refactor
# Description: Analyzes a list of scores using map, filter, sorted, any, all.
# Language: Python 3.x
def analyze_scores(scores):
passing = list(filter(lambda s: s >= 70, scores))
gpa_scale = list(map(lambda s: round(s / 25, 2), scores))
ranked = sorted(scores, reverse=True)
perfect = any(s == 100 for s in scores)
all_pass = all(s >= 70 for s in scores)
return passing, gpa_scale, ranked, perfect, all_pass
def main():
scores = [55, 82, 91, 40, 76, 88, 100, 63, 74, 59]
passing, gpa_scale, ranked, perfect, all_pass = analyze_scores(scores)
print("Passing scores:", passing)
print("GPA scale:", gpa_scale)
print("Ranked (high to low):", ranked)
print("Original list unchanged:", scores)
print("Any perfect score?", perfect)
print("Did everyone pass?", all_pass)
if __name__ == "__main__":
main()
Sample Test Cases
Input |
Passing (โฅ70) |
Any 100? |
All passed? |
|---|---|---|---|
|
|
|
|
|
|
|
|
|
|
|
|
Lambda Functionsยถ
๐ AP CSP: AAP-2.G ๐ PCEP-adjacent
Explanation
A lambda is a small, unnamed (โanonymousโ) function written in a single line. Itโs Pythonโs shorthand for a function you only need once โ usually as an argument to another function like sorted(), map(), or filter(). A lambda can only contain one expression and it automatically returns the result of that expression.
square = lambda x: x ** 2
# Equivalent to:
def square(x):
return x ** 2
Key Words
Term |
Meaning |
|---|---|
Lambda / anonymous function |
A function with no name, defined inline with the |
Expression |
A single computation that produces a value |
|
Tells |
First-class function |
A function that can be passed around like any other value |
Example
students = [("Alice", 91), ("Bob", 76), ("Charlie", 88)]
by_score = sorted(students, key=lambda s: s[1], reverse=True)
print(by_score) # [('Alice', 91), ('Charlie', 88), ('Bob', 76)]
๐ค Thinking Question โ check your answer
Question: Why canโt a lambda replace a def function that needs to print three different values and then return a result?
Answer: A lambda is restricted to a single expression โ no print() statements, no multiple lines, no loops, no if/else blocks (only a limited conditional expression). Anything that needs multiple steps or side effects belongs in a real def function.
Best Practices
Keep lambdas short โ one expression.
Never assign a lambda to a variable just to call it later โ thatโs what
defis for.The most common, most useful pattern is
sorted(iterable, key=lambda item: ...).
Activity: Sort It Your Wayยถ
Given a list of dictionaries representing books (title, author, year, pages), write three separate sorted() calls, each using a different lambda key:
Sort by
year, oldest first.Sort by
pages, longest first (reverse=True).Sort by the length of the title (
len(title)), shortest first.
Print each sorted list with a clear label before it.
AP CSP: AAP-2.G
Actual Program with Test Samples
# Assignment: Lambda Functions โ Sort It Your Way
# Description: Sorts a list of book dictionaries three different ways using lambda keys.
# Language: Python 3.x
books = [
{"title": "Dune", "author": "Herbert", "year": 1965, "pages": 412},
{"title": "1984", "author": "Orwell", "year": 1949, "pages": 328},
{"title": "The Hobbit", "author": "Tolkien", "year": 1937, "pages": 310},
]
by_year = sorted(books, key=lambda b: b["year"])
by_pages = sorted(books, key=lambda b: b["pages"], reverse=True)
by_title_len = sorted(books, key=lambda b: len(b["title"]))
print("By year (oldest first):")
for b in by_year:
print(f" {b['year']} โ {b['title']}")
print("By pages (longest first):")
for b in by_pages:
print(f" {b['pages']}pp โ {b['title']}")
print("By title length (shortest first):")
for b in by_title_len:
print(f" {len(b['title'])} chars โ {b['title']}")
Sample Test Cases
Sort Key |
First Result |
Last Result |
|---|---|---|
|
The Hobbit (1937) |
Dune (1965) |
|
Dune (412pp) |
The Hobbit (310pp) |
|
1984 (4 chars) |
The Hobbit (10 chars) |
Frozensetsยถ
๐ AP CSP: AAP-3.A, AAP-3.B ๐ PCEP-adjacent
Explanation
A frozenset is exactly what it sounds like: a set thatโs been frozen โ immutable, just like a tuple is an immutable list. Once created, you cannot add, remove, or change its contents.
colors = frozenset(["red", "green", "blue"])
# colors.add("yellow") # AttributeError โ frozensets have no .add()
When Would You Use This?
Use a frozenset any time you want the uniqueness and fast-lookup benefits of a set, but need the collection to be unchangeable โ most often because itโs being used as a dictionary key (regular sets canโt be dict keys; frozensets can).
schedule = {
frozenset(["Mon", "Wed", "Fri"]): "Math",
frozenset(["Tue", "Thu"]): "Science",
}
print(schedule[frozenset(["Tue", "Thu"])]) # Science
Key Words
Term |
Meaning |
|---|---|
Immutable |
Cannot be changed after creation |
Hashable |
Can be used as a dictionary key or set member (frozensets are hashable; sets are not) |
Set operations |
|
๐ค Thinking Question โ check your answer
Question: Why can a frozenset be used as a dictionary key, but a regular set cannot?
Answer: Dictionary keys must be hashable โ Python needs to compute a stable hash value for a key that never changes for the life of the object. A regular set is mutable (you could .add() to it after using it as a key, which would break the hash), so Python disallows it as a key. A frozenset is guaranteed immutable, so its hash is stable, making it a legal dictionary key.
Best Practices
Use
setfor a collection you plan to modify; usefrozensetfor a collection meant to stay constant.Reach for a
frozensetspecifically when you need to use a collection as a dictionary key.
Activity: Class Roster Overlapยถ
Two teachers each have a frozenset of student names in their AP CSP class:
period_1 = frozenset(["Ava", "Liam", "Noah", "Emma", "Mia"])
period_3 = frozenset(["Noah", "Emma", "Sofia", "Lucas"])
Write a program that prints:
Students in both periods (
&)Students in either period, with no duplicates (
|)Students only in Period 1, not Period 3 (
-)Students in exactly one of the two periods, not both (
^)Whether the two rosters share any student at all
AP CSP: AAP-3.A, AAP-3.B
Actual Program with Test Samples
# Assignment: Frozensets โ Class Roster Overlap
# Description: Compares two class rosters using frozenset operations.
# Language: Python 3.x
def compare_rosters(period_1, period_3):
both = period_1 & period_3
either = period_1 | period_3
only_p1 = period_1 - period_3
exactly_one = period_1 ^ period_3
overlaps = len(both) > 0
return both, either, only_p1, exactly_one, overlaps
def main():
period_1 = frozenset(["Ava", "Liam", "Noah", "Emma", "Mia"])
period_3 = frozenset(["Noah", "Emma", "Sofia", "Lucas"])
both, either, only_p1, exactly_one, overlaps = compare_rosters(period_1, period_3)
print("In both periods:", both)
print("In either period:", either)
print("Only in Period 1:", only_p1)
print("In exactly one period:", exactly_one)
print("Do the rosters overlap?", overlaps)
if __name__ == "__main__":
main()
Sample Test Cases
Operation |
Result |
|---|---|
|
|
|
|
|
|
overlap check |
|
Exception Hierarchy Deep Diveยถ
๐ PCEP 4.3 โ Python Built-In Exceptions Hierarchy
Section 4 back in the fall covered practical try/except and the common exceptions table โ thatโs everything you needed for the CPT. The PCEP exam goes one level deeper and expects you to know the actual class hierarchy exceptions are organized into.
BaseException
โโโ SystemExit
โโโ KeyboardInterrupt
โโโ Exception
โโโ ArithmeticError
โ โโโ ZeroDivisionError
โโโ LookupError
โ โโโ IndexError
โ โโโ KeyError
โโโ TypeError
โโโ ValueError
โโโ FileNotFoundError
PCEP Exam Tip: Order
exceptblocks from most specific to most general. Python checks them in order and runs the first match โ this is whyexcept ValueErrormust come beforeexcept Exceptionin the sametryblock, never after.
try:
x = int("abc")
except ValueError: # caught here โ most specific
print("Value error")
except Exception: # broader fallback
print("Some error")
๐ค Thinking Question โ check your answer
Question: ZeroDivisionError and IndexError are both subclasses of Exception, but theyโre not subclasses of each other. Looking at the hierarchy tree above, whatโs the closest common ancestor they share?
Answer: Exception itself โ ZeroDivisionError descends through ArithmeticError, and IndexError descends through LookupError, two separate branches that only reconnect at Exception. That means a single except Exception: block would catch both, but nothing more specific shared between them would.
Culminating Project: Library Catalog Analyzerยถ
Build a program that reads a small library catalog from a text file, analyzes it using built-in functions and lambdas, and uses frozensets to answer genre questions โ pulling together File Operations (Semester 1), Built-In Functions, Lambda Functions, and Frozensets (all above) into one working project. This is the natural capstone of the PCEP Certification Path.
Setup โ catalog.txt format: one book per line, comma-separated: title,author,year,genre1|genre2
Dune,Frank Herbert,1965,Sci-Fi|Adventure
1984,George Orwell,1949,Sci-Fi|Dystopian
The Hobbit,J.R.R. Tolkien,1937,Fantasy|Adventure
Klara and the Sun,Kazuo Ishiguro,2021,Sci-Fi|Drama
๐งฉ Scaffolding โ build it in this order:
Write
load_catalog()alone and print the raw list of dictionaries it produces.Add the
sorted()/filter()/map()lines one at a time, printing after each.Add the frozenset union/intersection logic.
Write
save_report()last.
Requirements:
Write
load_catalog(filename)that readscatalog.txtline by line (usingwith, wrapped intry/except FileNotFoundError) and returns a list of dictionaries, each with keys"title","author","year"(asint), and"genres"(as a frozenset built by splitting on|).Use
sorted()with alambdakey to print the catalog sorted by year, oldest first.Use
filter()with alambdato print only books published after 2000.Use
map()with alambdato print a list of just the titles, in upper case.Use frozenset operations to find and print:
Every unique genre across the whole catalog (union
|of all booksโ genre frozensets)All books that include both โSci-Fiโ and โAdventureโ as genres
Write
save_report(filename, catalog)that writes a summary report toreport.txtโ total book count, oldest and newest year, and the full unique-genre list โ using awithblock in"w"mode.Include a full program header block.
Actual Program with Test Samples
# Assignment: Culminating Project โ Library Catalog Analyzer
# Description: Reads a book catalog from file, analyzes it with map/filter/sorted/lambda,
# and uses frozensets to compare genres, then writes a summary report.
# Language: Python 3.x
def load_catalog(filename):
catalog = []
try:
with open(filename, "r") as file:
for line in file:
title, author, year, genres = line.strip().split(",")
catalog.append({
"title": title,
"author": author,
"year": int(year),
"genres": frozenset(genres.split("|")),
})
except FileNotFoundError:
print(f"{filename} not found โ starting with an empty catalog.")
return catalog
def save_report(filename, catalog, all_genres):
years = [book["year"] for book in catalog]
with open(filename, "w") as file:
file.write(f"Total books: {len(catalog)}\n")
file.write(f"Oldest year: {min(years) if years else 'N/A'}\n")
file.write(f"Newest year: {max(years) if years else 'N/A'}\n")
file.write(f"Unique genres: {sorted(all_genres)}\n")
def main():
catalog = load_catalog("catalog.txt")
if not catalog:
return
by_year = sorted(catalog, key=lambda b: b["year"])
modern = list(filter(lambda b: b["year"] > 2000, catalog))
titles = list(map(lambda b: b["title"].upper(), catalog))
all_genres = frozenset()
for book in catalog:
all_genres = all_genres | book["genres"]
scifi_adventure = [
b["title"] for b in catalog
if frozenset(["Sci-Fi", "Adventure"]) <= b["genres"]
]
print("Sorted by year:", [b["title"] for b in by_year])
print("Published after 2000:", [b["title"] for b in modern])
print("Titles (upper case):", titles)
print("All unique genres:", sorted(all_genres))
print("Sci-Fi AND Adventure:", scifi_adventure)
save_report("report.txt", catalog, all_genres)
if __name__ == "__main__":
main()
Sample Test Cases (using the four-book catalog.txt shown above)
Check |
Result |
|---|---|
Sorted by year (oldest first) |
|
Published after 2000 |
|
Unique genres (union of all frozensets) |
|
Books with both Sci-Fi and Adventure |
|
|
|
Stretch challenge: Add a most_common_genre() function that uses max() with a key=lambda to find which single genre appears in the most books โ without importing any extra modules.
AP CSP: AAP-3.A, AAP-3.B, AAP-2.G, CRD-2.B, CRD-2.J
Certification Alignment Referenceยถ
PCEP-30-02 Exam Topic Mapยถ
PCEP Objective |
Topics |
Covered In |
|---|---|---|
1.1 |
Interpreter, compiler, lexis, syntax, semantics |
Section 1 โ How Python Works (Semester 1) |
1.2 |
Keywords, indentation, comments |
Section 1 โ Python Structure (Semester 1) |
1.3 |
Literals, variables, numeral systems, PEP-8 |
Section 1 (Semester 1); Numeral Systems (Semester 2) |
1.4 |
Operators, precedence, Boolean, bitwise, type casting |
Section 1 โ Operators (Semester 1); Bitwise Operators (Semester 2) |
1.5 |
|
Section 1 โ I/O (Semester 1) |
2.1 |
|
Section 2 โ Conditionals (Semester 1) |
2.2 |
|
Section 2 โ Loops (Semester 1) |
3.1 |
Lists, indexing, slicing, methods, comprehensions, 2D lists |
Section 3 โ Lists (Semester 1) |
3.2 |
Tuples, immutability, nesting |
Section 3 โ Tuples (Semester 1) |
3.3 |
Dictionaries, keys/values/items, iteration |
Section 3 โ Dictionaries (Semester 1) |
3.4 |
Strings, indexing, slicing, escaping, methods |
Section 3 โ Strings (Semester 1) |
4.1 |
Functions, |
Section 4 โ Functions (Semester 1) |
4.2 |
Parameters, arguments, defaults, scope, |
Section 4 โ Functions (Semester 1) |
4.3 |
Exception hierarchy: |
Exception Hierarchy Deep Dive (Semester 2) |
4.4 |
|
Section 4 โ Exceptions (Semester 1) |
Supplemental |
Built-in functions ( |
PCEP Certification Path โ Built-In Functions (Semester 2) |
Supplemental |
Lambda (anonymous) functions |
PCEP Certification Path โ Lambda Functions (Semester 2) |
Supplemental |
Frozensets (immutable sets) |
PCEP Certification Path โ Frozensets (Semester 2) |
Supplemental |
File I/O ( |
File Operations (Semester 1) |
Rows marked Supplemental arenโt separately numbered objectives in the official PCEP-30-02 exam blocks (file I/O in particular is tested at the next certification level, PCAP) โ theyโre included because theyโre core Python fluency and directly support both the CPT and the PCEP exam.
AP CSP Standards Alignmentยถ
AP CSP Standard |
Description |
Covered In |
|---|---|---|
CRD-2.A |
Program design and development |
Throughout |
CRD-2.B |
Implement algorithms |
Functions, OOP, Projects |
CRD-2.G |
Call procedures |
Functions |
CRD-2.J |
Test and debug |
Debugging, Exception Handling, File Operations |
AAP-2.E |
Sequencing, selection, iteration |
Control Flow |
AAP-2.F |
Mathematical operations |
Operators |
AAP-2.G |
Abstraction to manage complexity |
Functions, Modules, OOP, Built-In Functions (Semester 2), Lambda Functions (Semester 2) |
AAP-2.K |
List iteration |
Lists |
AAP-3.A |
Collect and represent data |
Data Structures, File Operations, Frozensets (Semester 2) |
AAP-3.B |
Use abstractions to organize data |
Dictionaries, OOP, Lists, Frozensets (Semester 2) |
AAP-3.C |
Analyze data to draw conclusions |
Data Structures |
AAP-4.A |
Data abstractions for complexity |
Lists, 2D Lists, Classes |
Document maintained for AP CSP and PCEP-30-02 certification alignment. Reorganized August 2026 โ CSP-first, PCEP-Second Semester structure.