✏️ 课堂习题

第 7 章 · 面向对象编程 · 共 10 题 · 内容覆盖讲稿全部知识点

答案默认折叠,点击题目或右侧开关控制显示
展开所有答案
1
定义一个 Student 类,包含 name(姓名)和 score(分数)两个属性,编写 __init__ 初始化方法。再添加一个 show_info 方法,打印学生信息。创建两个学生对象并调用方法。
参考答案
python
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
2
以下代码中 print(stu.__age) 会报什么错误?为什么?在类内部如何正确访问 __age
参考答案
python
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)来间接访问私有属性——这就是封装的思想。

3
定义一个 Car 类,使用 __slots__ 限制只能有 brandcolor 两个属性。尝试给 car 对象动态添加 speed 属性,观察结果并解释原因。
参考答案
python
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__ 可以防止意外添加属性,同时也能节省内存。

4
定义一个 Circle 类,包含 radius(半径)属性。添加一个静态方法 calc_area(r),接收半径参数返回圆的面积(π取3.14)。分别通过类名和对象两种方式调用该静态方法。
参考答案
python
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
5
定义一个 Rectangle 类,包含 widthheight 属性。使用 @property 装饰器将 area(面积)和 perimeter(周长)定义为属性(而非方法),使得调用时无需加括号。
参考答案
python
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.arear.perimeter 时不需要加括号,代码更加简洁优雅。

6
阅读以下继承代码,写出输出结果。解释 super().__init__()super().speak() 的作用。
参考答案
python
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 方法,调用相同的方法名但输出不同内容——这就是多态

7
定义一个 Book 类,包含 title(书名)和 price(价格)。通过重载 __lt__ 方法使得两个 Book 对象可以直接用 < 比较价格。创建两本书并排序。
参考答案
python
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__ 等。

8
判断题:以下关于面向对象的说法是否正确?请逐一判断并说明理由。
(1) Python 中类的所有属性默认都是公开的
(2) 静态方法可以通过类名调用,不能通过对象调用
(3) 子类可以访问父类的私有属性
(4) 多态就是子类重写父类的方法
参考答案

(1) 正确。Python 中属性默认都是公开的,没有类似 Java 的 private 关键字。以 __ 开头只是命名约定,实际上仍可通过 _ClassName__attr 访问。

(2) 错误。静态方法既可以通过类名调用,也可以通过对象调用。

(3) 错误。子类不能直接访问父类的私有属性(__开头),只能通过父类提供的公开方法间接访问。

(4) 部分正确。多态的核心是"调用相同方法,不同对象做不同事情"。子类重写父类方法是实现多态的一种方式,但多态的内涵更为广泛。

9
以下代码定义了一个抽象类 Shape,请补全 CircleRectangle 子类,实现 get_area 方法。最后计算列表中所有图形的总面积。
参考答案
python
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 计算总面积。

10
阅读以下工资结算代码,回答:(1) 为什么 Employee 类不能直接实例化?(2) isinstance 和 type 在判断类型时有什么区别?(3) 如果新增一个 Intern(实习生)类,月薪固定 3000 元,如何修改代码?
参考答案
python
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 即可,无需修改已有代码——这体现了面向对象的开闭原则(对扩展开放,对修改封闭)。