Full Worked Solutions · AP CSA-Feeder · US / ON / BC / AB Styles完整解析 · AP CSA 衔接 · 美 / 安 / 卑 / 阿省风格
(B) fido is an instance of the Dog class (blueprint).(B) fido 是 Dog 类(蓝图)的一个实例。
Reasoning: class Dog: defines the blueprint. fido = Dog("Fido", 3) creates one concrete object (instance) from that blueprint. Dog is not an instance of anything here; it is the class definition itself.理由:class Dog: 定义蓝图。fido = Dog("Fido", 3) 从该蓝图创建一个具体对象(实例)。Dog 不是任何东西的实例,它本身就是类定义。
(C) The specific object on which the method was called.(C) 调用该方法的具体对象。
Reasoning: when you write s1.is_passing(), Python automatically passes s1 as the self argument. Inside the method, self refers to s1. For s2.is_passing(), self would be s2. You never pass self explicitly when calling the method.理由:写 s1.is_passing() 时,Python 自动将 s1 作为 self 参数传入。方法内部,self 指代 s1。调用 s2.is_passing() 时,self 则是 s2。调用方法时永远不要手动传入 self。
self explicitly. Python inserts it. The first argument the caller supplies goes to the second parameter in the method signature (after self).选项 (D) 是经典混淆:调用者不显式传入 self,Python 自动插入。调用者提供的第一个实参对应方法签名中的第二个参数(在 self 之后)。a.name = "Whiskers". The line a.age = 5 only modified the age attribute; name was not touched.a.name = "Whiskers"。a.age = 5 只修改了 age 属性;name 未被改动。
b.age = 2. Object b was created with age=2 and was never modified.b.age = 2。对象 b 创建时 age=2,且从未被修改。
Each object has its own independent copy of every instance attribute, so changing a.age only modifies the attribute stored inside object a, leaving b's copy untouched.每个对象都有每个实例属性的独立副本,因此修改 a.age 只改变对象 a 内部存储的属性,不影响 b 的副本。
cat1_age and cat2_age are always at risk of being overwritten by any code in the program; instance attributes live inside objects and are protected by scope.实例属性的独立性是 OOP 相较过程式全局变量的核心优势。两个全局变量 cat1_age 和 cat2_age 随时可能被程序中任何代码覆盖;实例属性存于对象内部,受作用域保护。Step-by-step trace of c.count:逐步追踪 c.count:
c = Counter(10): count = 10 (via default start)c = Counter(10):count = 10(通过 start 参数)c.increment(): count = 10 + 1 = 11 (default amount=1)c.increment():count = 10 + 1 = 11(默认 amount=1)c.increment(3): count = 11 + 3 = 14c.increment(3):count = 11 + 3 = 14c.reset(): count = 0c.reset():count = 0First print: 14 (after increment then increment(3)).
Second print: 0 (after reset).第一次打印:14(两次 increment 后)。
第二次打印:0(reset 后)。
increment: mutator - it changes (modifies) self.count.reset: mutator - it changes self.count to 0.get_count: accessor - it only reads and returns self.count without modifying it.increment:修改器 - 修改 self.count。reset:修改器 - 将 self.count 改为 0。get_count:访问器 - 只读取并返回 self.count,不修改它。
increment(self, amount=1)) can be called with or without that argument. c.increment() uses the default 1; c.increment(3) overrides it with 3. This is a clean pattern for "increment by 1 usually, but by N when needed."带默认参数的方法(如 increment(self, amount=1))可以有参或无参调用。c.increment() 使用默认值 1;c.increment(3) 用 3 覆盖。这是"通常加 1,需要时加 N"的简洁模式。For r1 = Rectangle(6, 4): r1.width = 6, r1.height = 4.对于 r1 = Rectangle(6, 4):r1.width = 6,r1.height = 4。
r1.area() returns 24 (6 x 4 = 24).r2.area() returns 25 (5 x 5 = 25).r1.area() 返回 24(6 x 4 = 24)。r2.area() 返回 25(5 x 5 = 25)。
r1.is_square() returns False: because r1.width = 6 and r1.height = 4, and 6 != 4.r2.is_square() returns True: because r2.width = 5 and r2.height = 5, and 5 == 5.r1.is_square() 返回 False:因为 r1.width = 6,r1.height = 4,6 != 4。r2.is_square() 返回 True:因为 r2.width = 5,r2.height = 5,5 == 5。
Python automatically calls Rectangle.__init__(r1, 6, 4), which sets r1.width = 6 and r1.height = 4, fully initialising the new object before the assignment to r1 completes.Python 自动调用 Rectangle.__init__(r1, 6, 4),将 r1.width 设为 6,r1.height 设为 4,在赋值给 r1 完成之前完全初始化新对象。
__init__ is called the "constructor" because it constructs (builds) the object's initial state. You never call it directly: writing r1.__init__(6, 4) would re-initialise an already-created object, which is almost always a mistake.__init__ 被称为"构造函数",因为它构建对象的初始状态。永远不要直接调用它:写 r1.__init__(6, 4) 会重新初始化一个已创建的对象,这几乎总是错误的。After all four client lines: s1.grade = 85 (updated from 78 by s1.update_grade(85)), s2.grade = 55 (updated from 42 by s2.update_grade(55)).四行客户代码执行后:s1.grade = 85(由 s1.update_grade(85) 从 78 更新),s2.grade = 55(由 s2.update_grade(55) 从 42 更新)。
s1.is_passing() returns True: 85 ≥ 50.s2.is_passing() returns True: 55 ≥ 50.s1.is_passing() 返回 True:85 ≥ 50。s2.is_passing() 返回 True:55 ≥ 50。
def letter_grade(self):
if self.grade >= 80:
return "A"
elif self.grade >= 70:
return "B"
elif self.grade >= 60:
return "C"
elif self.grade >= 50:
return "D"
else:
return "F"
update_grade is a mutator because it changes (mutates) the self.grade attribute; is_passing is an accessor because it only reads self.grade and returns a value without modifying any attribute.update_grade 是修改器,因为它改变(修改)了 self.grade 属性;is_passing 是访问器,因为它只读取 self.grade 并返回一个值,不修改任何属性。
letter_grade method uses cascaded elif (not nested if) because once a condition is met the function returns immediately. Cascaded elif is cleaner and more efficient than checking all ranges independently.letter_grade 方法使用级联 elif(而非嵌套 if),因为一旦条件满足函数立即返回。级联 elif 比独立检查所有范围更简洁高效。Trace of acc (started with owner="Carol", balance=500):追踪 acc(初始 owner="Carol",balance=500):
acc.deposit(200): balance = 500 + 200 = 700. Nothing printed.balance = 500 + 200 = 700。无打印。acc.withdraw(100): 100 ≤ 700, so balance = 700 - 100 = 600. Nothing printed.100 ≤ 700,所以 balance = 700 - 100 = 600。无打印。acc.withdraw(700): 700 > 600, so prints Insufficient funds. Balance stays 600.700 > 600,故打印 Insufficient funds。余额保持 600。Text printed during calls: Insufficient funds (once, on the third withdraw).调用期间打印内容:Insufficient funds(一次,第三次取款时)。
print(acc.get_balance()) outputs 600.print(acc.get_balance()) 输出 600。
david = BankAccount("David")david = BankAccount("David")
The withdraw method checks amount ≤ self.balance before subtracting, so external code cannot directly set a negative balance; the guard inside the method enforces the constraint that the balance can never go below zero, which is encapsulation in action.withdraw 方法在扣款前检查 amount ≤ self.balance,因此外部代码无法直接设置负余额;方法内的保护逻辑强制执行余额不低于零的约束,这正是封装的体现。
BankAccount("David") and BankAccount("David", 0) produce identical objects because balance=0 is the default. This reduces code duplication for the common case.默认参数允许灵活创建对象。BankAccount("David") 和 BankAccount("David", 0) 产生相同的对象,因为 balance=0 是默认值。这减少了常见情况下的代码重复。total = 88 + 45 + 72 + 50 = 255.avg = 255 / 4 = 63.75.total = 88 + 45 + 72 + 50 = 255。avg = 255 / 4 = 63.75。
failing = ["Bob"].
Bob's grade (45) is the only one below 50; Alice (88), Carol (72), David (50) all pass.failing = ["Bob"]。
Bob 的成绩(45)是唯一低于 50 的;Alice(88)、Carol(72)、David(50)均通过。
top = max(roster, key=lambda s: s.grade)
(This returns the Student object for Alice, whose grade 88 is the highest.)(这返回 Alice 的 Student 对象,她的成绩 88 最高。)
Using objects bundles each student's name and score together so methods like is_passing() can be called directly on each object; with two parallel lists, you must always keep the same index in sync between lists, which is error-prone.使用对象将每位学生的姓名和成绩捆绑在一起,可以直接对每个对象调用 is_passing() 等方法;使用两个平行列表时必须始终保持两个列表的索引同步,容易出错。
sum(s.grade for s in roster) is a concise way to iterate a list of objects and extract an attribute. Think of it as a for loop that returns a value: "for each student in the roster, give me their grade, then sum all those grades."生成器表达式 sum(s.grade for s in roster) 是遍历对象列表并提取属性的简洁方式。可以将其理解为一个返回值的 for 循环:"对名单中每个学生,给我他们的成绩,然后求和。"Trace of savings._balance:savings._balance 追踪:
SavingsAccount("Emma", 1000, 0.05): _balance = 1000, rate = 0.05.SavingsAccount("Emma", 1000, 0.05):_balance = 1000,rate = 0.05。savings.deposit(200): _balance = 1000 + 200 = 1200 (inherited method from BankAccount).savings.deposit(200):_balance = 1000 + 200 = 1200(继承自 BankAccount 的方法)。savings.add_interest(): _balance = 1200 * (1 + 0.05) = 1200 * 1.05 = 1260.0.savings.add_interest():_balance = 1200 * (1 + 0.05) = 1200 * 1.05 = 1260.0。super().__init__(owner, balance) calls the parent class (BankAccount) constructor from inside the child (SavingsAccount) constructor, which sets up self.owner and self._balance before SavingsAccount.__init__ adds self.rate.super().__init__(owner, balance) 在子类(SavingsAccount)构造函数内调用父类(BankAccount)构造函数,从而在 SavingsAccount.__init__ 添加 self.rate 之前,先设置 self.owner 和 self._balance。
The leading underscore in _balance is Python's convention for "treat as private": it signals to other programmers that _balance should not be accessed directly from outside the class, but instead through the deposit(), withdraw(), and get_balance() methods; this is encapsulation because the class controls how its internal data is read and modified._balance 的前置下划线是 Python"视为私有"的惯例:它向其他程序员表明 _balance 不应从类外部直接访问,而应通过 deposit()、withdraw() 和 get_balance() 方法访问;这就是封装,因为类控制着内部数据的读取和修改方式。
savings.deposit(200) works even though deposit is not defined in SavingsAccount. Python looks up the method in the parent class (BankAccount) automatically. This is "inherited behaviour": subclasses get parent methods for free and only need to define what is new or different.注意 savings.deposit(200) 可以工作,尽管 deposit 未在 SavingsAccount 中定义。Python 会自动在父类(BankAccount)中查找方法。这就是"继承行为":子类免费获得父类方法,只需定义新增或不同的内容。for s in students:
if not s.passed():
print(s.name)
Names printed: Ben (score 55, 55 < 60), Eve (score 48, 48 < 60).
Amy (75), Cleo (90), Dan (60) all satisfy score ≥ 60 and are not printed.打印的姓名:Ben(分数 55,55 < 60),Eve(分数 48,48 < 60)。
Amy(75)、Cleo(90)、Dan(60)均满足 score ≥ 60,不打印。
total = sum(s.score for s in students)
avg = total / len(students)
print(avg)
Arithmetic: 75 + 55 + 90 + 60 + 48 = 328; 328 / 5 = 65.6. Output: 65.6.计算过程:75 + 55 + 90 + 60 + 48 = 328;328 / 5 = 65.6。输出:65.6。
With a list of objects, each student's name and score are bundled together so you can add a new student as a single Student(...) call without touching two separate lists; with parallel lists, you must append to both lists in the correct order, and any mismatch corrupts all future lookups.使用对象列表时,每位学生的姓名和分数捆绑在一起,只需一次 Student(...) 调用即可添加新学生,无需同时操作两个独立列表;使用平行列表时,必须以正确顺序向两个列表各自追加,任何错位都会损坏所有后续查询。
60 ≥ 60 is True) because passed() uses >=, not >. Boundary conditions like this are classic exam trick questions: always check whether the boundary is inclusive (≥) or exclusive (>).Dan 的分数 60 通过了(60 ≥ 60 为 True),因为 passed() 使用 >= 而非 >。这类边界条件是经典考试陷阱:始终检查边界是包含(≥)还是不包含(>)。class Product:
def __init__(self, name, price, quantity):
self.name = name
self.price = price
self.quantity = quantity
def total_value(self):
return self.price * self.quantity
def restock(self, amount):
self.quantity += amount
def sell(self, amount):
if amount <= self.quantity:
self.quantity -= amount
else:
print("Not enough stock")
Create and sell:创建并销售:
widget = Product("Widget", 2.50, 10)
widget.sell(15)
sell(15): checks 15 ≤ 10 which is False, so prints Not enough stock. quantity remains 10.sell(15):检查 15 ≤ 10 为 False,故打印 Not enough stock。quantity 保持 10。
Mutators: restock and sell (both modify self.quantity).
Accessor: total_value (reads self.price and self.quantity and returns a computed value without changing any attribute).修改器:restock 和 sell(均修改 self.quantity)。
访问器:total_value(读取 self.price 和 self.quantity 并返回计算值,不修改任何属性)。
sell (if amount ≤ self.quantity) mirrors the guard in BankAccount.withdraw from Q7. This pattern - "check validity before modifying" - is a standard encapsulation technique that protects the object from reaching an invalid state (negative quantity).sell 中的保护(if amount ≤ self.quantity)与 Q7 中 BankAccount.withdraw 的保护相同。这种模式 - "修改前检查有效性" - 是标准的封装技术,保护对象不进入无效状态(负数库存)。Two problems with the procedural approach at 100 vehicles:
1. You need 300 separate variables (car1_make through car100_km), making the code extremely long and hard to read.
2. There is no way to pass all data about one vehicle as a single unit to a function; you would need to pass three separate arguments for each vehicle, and it is easy to mix up which variables belong to which car.过程式方式在 100 辆车时的两个问题:
1. 需要 300 个独立变量(car1_make 到 car100_km),代码极长且难以阅读。
2. 无法将一辆车的所有数据作为单一单元传给函数;每辆车需要传入三个独立参数,容易混淆哪些变量属于哪辆车。
class Vehicle:
def __init__(self, make, year, km):
self.make = make
self.year = year
self.km = km
def add_km(self, distance):
self.km += distance
class ElectricVehicle(Vehicle):
def __init__(self, make, year, km, battery_kwh):
super().__init__(make, year, km)
self.battery_kwh = battery_kwh
def charge(self, kwh):
self.battery_kwh += kwh
ElectricVehicle IS-A Vehicle, so inheritance is the right design. It would be wrong to design it as a has-a (composition), because that would mean a vehicle "contains" another vehicle, which makes no sense. Always ask "is-a or has-a?" before choosing between inheritance and composition.is-a 规则:ElectricVehicle 是(IS-A)Vehicle,因此继承是正确设计。将其设计为 has-a(组合)是错误的,因为那意味着一辆车"包含"另一辆车,毫无意义。选择继承还是组合前,始终问自己"是 is-a 还是 has-a?"