✏️ 课堂习题
第 7 章 · 面向对象编程 · 共 10 题 · 内容覆盖讲稿全部知识点
Student 类,包含 name(姓名)和 score(分数)两个属性,编写 __init__ 初始化方法。再添加一个 show_info 方法,打印学生信息。创建两个学生对象并调用方法。class Student:
def __init__(self, name, score):
self.name = name
self.score = score
def show_info(self):
print(f'姓名: {self.name}, 分数: {self.score}')
s1 = Student('张三', 88)
s2 = Student('李四', 95)
s1.show_info() # 姓名: 张三, 分数: 88
s2.show_info() # 姓名: 李四, 分数: 95
print(stu.__age) 会报什么错误?为什么?在类内部如何正确访问 __age?class Student:
def __init__(self, name, age):
self.name = name
self.__age = age
def get_age(self):
return self.__age # 正确:类内部通过 self.__age 访问
stu = Student('王大锤', 20)
# print(stu.__age) # AttributeError: 'Student' object has no attribute '__age'
print(stu.get_age()) # 20 — 正确方式
解释:以 __(双下划线)开头的属性是私有属性,类外部无法直接访问,会引发 AttributeError 异常。正确的做法是在类内部提供公开的方法(如 get_age)来间接访问私有属性——这就是封装的思想。
Car 类,使用 __slots__ 限制只能有 brand 和 color 两个属性。尝试给 car 对象动态添加 speed 属性,观察结果并解释原因。class Car:
__slots__ = ('brand', 'color')
def __init__(self, brand, color):
self.brand = brand
self.color = color
car = Car('Tesla', '红色')
print(car.brand, car.color) # Tesla 红色
# car.speed = 200 # AttributeError: 'Car' object has no attribute 'speed'
解释:__slots__ 魔法限制了该类的对象只能有指定的属性。试图动态添加 speed 属性时会引发 AttributeError。使用 __slots__ 可以防止意外添加属性,同时也能节省内存。
Circle 类,包含 radius(半径)属性。添加一个静态方法 calc_area(r),接收半径参数返回圆的面积(π取3.14)。分别通过类名和对象两种方式调用该静态方法。class Circle:
def __init__(self, radius):
self.radius = radius
@staticmethod
def calc_area(r):
"""静态方法:计算圆的面积"""
return 3.14 * r * r
# 通过类名调用
print(Circle.calc_area(5)) # 78.5
# 通过对象调用
c = Circle(5)
print(c.calc_area(10)) # 314.0
Rectangle 类,包含 width 和 height 属性。使用 @property 装饰器将 area(面积)和 perimeter(周长)定义为属性(而非方法),使得调用时无需加括号。class Rectangle:
def __init__(self, width, height):
self.width = width
self.height = height
@property
def area(self):
return self.width * self.height
@property
def perimeter(self):
return 2 * (self.width + self.height)
r = Rectangle(5, 3)
print(f'面积: {r.area}') # 15
print(f'周长: {r.perimeter}') # 16
解释:@property 装饰器将方法转为属性访问方式,调用 r.area 和 r.perimeter 时不需要加括号,代码更加简洁优雅。
super().__init__() 和 super().speak() 的作用。class Animal:
def __init__(self, name):
self.name = name
def speak(self):
return f'{self.name}发出声音'
class Dog(Animal):
def __init__(self, name, breed):
super().__init__(name) # 调用父类初始化
self.breed = breed
def speak(self):
return f'{self.name}({self.breed})汪汪叫'
class Cat(Animal):
def speak(self):
return f'{self.name}喵喵叫'
dog = Dog('旺财', '金毛')
cat = Cat('咪咪')
print(dog.speak()) # 旺财(金毛)汪汪叫
print(cat.speak()) # 咪咪喵喵叫
解释:super().__init__(name) 调用父类 Animal 的初始化方法,完成 name 属性的赋值(代码复用)。Dog 和 Cat 各自重写了 speak 方法,调用相同的方法名但输出不同内容——这就是多态。
Book 类,包含 title(书名)和 price(价格)。通过重载 __lt__ 方法使得两个 Book 对象可以直接用 < 比较价格。创建两本书并排序。class Book:
def __init__(self, title, price):
self.title = title
self.price = price
def __lt__(self, other):
return self.price < other.price
def __repr__(self):
return f'{self.title}(¥{self.price})'
books = [Book('Python编程', 59.8), Book('数据科学', 78.0), Book('算法导论', 49.0)]
books.sort() # 使用 __lt__ 比较
print(books) # [算法导论(¥49.0), Python编程(¥59.8), 数据科学(¥78.0)]
解释:运算符重载通过定义 __lt__(less than)魔法方法实现,使得 sort() 可以按价格对 Book 对象排序。类似的方法还有 __gt__、__eq__ 等。
(1) Python 中类的所有属性默认都是公开的
(2) 静态方法可以通过类名调用,不能通过对象调用
(3) 子类可以访问父类的私有属性
(4) 多态就是子类重写父类的方法
(1) 正确。Python 中属性默认都是公开的,没有类似 Java 的 private 关键字。以 __ 开头只是命名约定,实际上仍可通过 _ClassName__attr 访问。
(2) 错误。静态方法既可以通过类名调用,也可以通过对象调用。
(3) 错误。子类不能直接访问父类的私有属性(__开头),只能通过父类提供的公开方法间接访问。
(4) 部分正确。多态的核心是"调用相同方法,不同对象做不同事情"。子类重写父类方法是实现多态的一种方式,但多态的内涵更为广泛。
Shape,请补全 Circle 和 Rectangle 子类,实现 get_area 方法。最后计算列表中所有图形的总面积。from abc import ABCMeta, abstractmethod
class Shape(metaclass=ABCMeta):
@abstractmethod
def get_area(self):
pass
class Circle(Shape):
def __init__(self, radius):
self.radius = radius
def get_area(self):
return 3.14 * self.radius ** 2
class Rectangle(Shape):
def __init__(self, width, height):
self.width = width
self.height = height
def get_area(self):
return self.width * self.height
shapes = [Circle(5), Rectangle(4, 6), Circle(3)]
total = sum(s.get_area() for s in shapes)
print(f'总面积: {total:.1f}') # 总面积: 130.8
解释:Shape 是抽象类,get_area 是抽象方法(只有声明没有实现)。Circle 和 Rectangle 必须各自实现 get_area 方法,否则无法实例化。这里通过多态遍历 shapes 列表,统一调用 get_area 计算总面积。
Intern(实习生)类,月薪固定 3000 元,如何修改代码?from abc import ABCMeta, abstractmethod
class Employee(metaclass=ABCMeta):
def __init__(self, name):
self.name = name
@abstractmethod
def get_salary(self):
pass
class Intern(Employee):
"""实习生"""
def get_salary(self):
return 3000.0
# emp = Employee('张三') # TypeError: 不能实例化抽象类
# isinstance vs type:
emp = Intern('小白')
print(type(emp) == Employee) # False(精准匹配)
print(isinstance(emp, Employee)) # True(模糊匹配,包含子类)
# 使用示例
emps = [Intern('小白'), Intern('小新')]
for emp in emps:
print(f'{emp.name}工资: ¥{emp.get_salary()}元')
(1) 因为 Employee 包含 @abstractmethod 抽象方法 get_salary,抽象类不能直接实例化。
(2) type() 是精准匹配——只返回对象本身的类;isinstance() 是模糊匹配——会检查整个继承链,子类对象也被认为是父类类型。
(3) 只需定义 Intern(Employee) 类并实现 get_salary 返回 3000.0 即可,无需修改已有代码——这体现了面向对象的开闭原则(对扩展开放,对修改封闭)。