python基础-面向对象编程(2)-练习

通过Python示例讲解面向对象编程:封装(Person类、TemperatureConverter类使用私有属性和@property 实现数据保护与转换)、继承(Vehicle与Car、SystemMonitor多重继承展示代码重用)、方法重写(Shape子类、Employee与Manager重写方法定制行为)、object类(Vector2D、Product重写特殊方法)和多态(车辆类、PaymentMethod抽象基类实现鸭子类型与接口统一)

Posted by Hilda on July 9, 2025

封装

习题1.1

创建一个 Person 类,包含 name 和 age 两个实例属性。

○ 将 age 属性设置为“私有”(使用双下划线 __age)。

○ 使用 @property 装饰器为 __age 提供一个 getter 方法,确保年龄获取时总是返回正整数。

○ 为 __age 提供一个 setter 方法,确保设置的年龄是介于 0 到 120 之间的整数。如果不在范围内,则打印警告并拒绝设置,或将其设置为有效默认值。

○ 创建 Person 对象并测试 age 属性的读写。

参考:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
# 博客地址: https://kirsten-1.github.io/
# 创建者: kirsten-1
# 时间: 2025/7/6  21:12


class Person:
    def __init__(self, name, age):
        # 直接调用setter方法,以便在初始化时也能应用验证逻辑
        self.__age = age
        self.name = name

    @property
    def age(self):
        return max(0, self.__age)

    @age.setter
    def age(self, value):
        if not isinstance(value, int):
            print(f"警告,年龄必须是整数,拒绝设置:{value}")
        elif not (0 <= value <= 120):
            print(f"警告,年龄必须介于0到120之间,拒绝设置:{value}")
        else:
            self.__age = value

    def display_info(self):
        print(f"姓名:{self.name}, 年龄:{self.age}")

p1 = Person("Adele", 30)
p1.display_info()

print(f"Adele的年龄是:{p1.age}")

p1.age = 32
p1.display_info()

p1.age = 130
p1.display_info()

p1.age = 25.5
p1.display_info()

image-20250707003433363

习题1.2

创建一个 TemperatureConverter 类,用于华氏度和摄氏度之间的转换。

○ 内部存储一个“私有”属性 __celsius (摄氏度)。

○ 使用 @property 装饰器,提供 celsius 属性的 getter 和 setter。

○ 提供一个 fahrenheit 属性的 getter 和 setter,当设置华氏度时,自动计算并更新内部的 __celsius 值;当获取华氏度时,自动从 __celsius 计算并返回。

■ 摄氏度转华氏度:F=Ctimes1.8+32

■ 华氏度转摄氏度:C=(F−32)/1.8

○ 创建对象并测试双向转换。


参考答案

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
# 博客地址: https://kirsten-1.github.io/
# 创建者: kirsten-1
# 时间: 2025/7/7  22:51


class TemperatureConverter:
    def __init__(self, celsius = 0):
        self.__celsius = celsius

    @property
    def celsius(self):
        print("获取摄氏度...")
        return self.__celsius

    @celsius.setter
    def celsius(self, value):
        if not isinstance(value, (int, float)):
            raise ValueError(f"警告:温度必须是数字,{value}是非法的!")
        print(f"设置摄氏度是:{value}")
        self.__celsius = value

    @property
    def fahrenheit(self):
        """
        根据内部存储的摄氏度计算并返回华氏度
        `F=Ctimes1.8+32`
        """
        print("获取华氏度...")
        return self.__celsius * 1.8 + 32

    @fahrenheit.setter
    def fahrenheit(self, value):
        """
        设置华氏度,并自动计算更新内部的 __celsius 值
        `C=(F−32)/1.8`
        """
        if not isinstance(value, (int, float)):
            raise ValueError(f"警告:温度必须是数字,{value}是非法的!")
        print(f"设置华氏度为: {value}℉")
        self.__celsius = (value - 32) / 1.8

# 初始化为25摄氏度
converter1 = TemperatureConverter(celsius=25)
print(f"对应的摄氏度:{converter1.celsius}")
print(f"对应的华氏度:{converter1.fahrenheit}")

# 设置华氏度为68
converter1.fahrenheit = 68
print(f"对应的摄氏度:{converter1.celsius}")
print(f"对应的华氏度:{converter1.fahrenheit}")

image-20250707230452346

继承

习题2.1

创建一个 Vehicle 父类,包含 make (品牌) 和 model (型号) 属性,以及一个 start_engine() 方法。

○ 创建一个 Car 子类,继承自 Vehicle。

○ 在 Car 类的 __init__ 方法中调用父类的 __init__ 方法,并添加一个 num_doors (车门数量) 属性。

○ 为 Car 类添加一个 honk() 方法。

○ 创建 Car 对象并测试所有方法。


参考答案:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
# 博客地址: https://kirsten-1.github.io/
# 创建者: kirsten-1
# 时间: 2025/7/8  13:22


class Vehicle:
    """
    包含 make (品牌) 和 model (型号) 属性,以及一个 `start_engine() `方法
    """
    def __init__(self, make, model):
        self.make = make
        self.model = model
        print(f"Vehicle {self.make}-{self.model} 实例被创建")

    def start_engine(self):
        print(f"Vehicle {self.make}-{self.model} start_engine方法被调用... ")

class Car(Vehicle):
    def __init__(self, make, model, num_doors = 4):
        super().__init__(make, model)
        self.num_doors = num_doors
        print(f"实例车 {self.make}-{self.model}被创建,车门数量是:{self.num_doors}")

    def honk(self):
        print(f"实例车 {self.make}-{self.model}  honk方法被调用")

c = Car("A", "B", 4)
c.start_engine()
c.honk()

image-20250708132857125

习题2.2

创建一个 Logger 类,包含一个 log(message) 方法。

○ 创建一个 Notifier 类,包含一个 notify(message) 方法。

○ 创建一个 SystemMonitor 类,多重继承 Logger 和 Notifier。

○ 在 SystemMonitor 的 __init__ 方法中,使用 super().__init__() (注意多重继承中 super() 的用法)确保所有父类的初始化逻辑被调用(如果它们有 __init__)。

○ 在 SystemMonitor 中添加一个 check_status() 方法,该方法调用 log() 和 notify()。

○ 创建 SystemMonitor 对象并测试 check_status()


注意下面2种写法,执行结果不一样:

第一种:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
# 博客地址: https://kirsten-1.github.io/
# 创建者: kirsten-1
# 时间: 2025/7/8  13:30

class Logger:
    def __init__(self):
        print("Logger构造函数被调用...")

    def log(self, message):
        print(f"Logger的消息:{message}")

class Notifier:
    def __init__(self):
        print("Notifier构造函数被调用...")

    def notify(self, message):
        print(f"Notifier的消息:{message}")

class SystemMonitor(Logger, Notifier):
    def __init__(self):
        super().__init__()
        print("SystemMonitor构造函数被调用...")

    def check_status(self, message):
        self.log(message)
        self.notify(message)

sys_monitor = SystemMonitor()
sys_monitor.check_status("设备运行良好")
print(SystemMonitor.__mro__)

image-20250708134415413

可以看到虽然SystemMonitor同时继承自Logger和Notifier,但是调用构造函数只调用了Logger,而没有调用Notifier。为了让所有父类的 __init__ 方法都被调用,需要确保 继承链上的所有类都使用 super().__init__() 来调用它们的下一个父类

写法2:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
# 博客地址: https://kirsten-1.github.io/
# 创建者: kirsten-1
# 时间: 2025/7/8  13:30

class Logger:
    def __init__(self):
        super().__init__()
        print("Logger构造函数被调用...")

    def log(self, message):
        print(f"Logger的消息:{message}")

class Notifier:
    def __init__(self):
        super().__init__()
        print("Notifier构造函数被调用...")

    def notify(self, message):
        print(f"Notifier的消息:{message}")

class SystemMonitor(Logger, Notifier):
    def __init__(self):
        super().__init__()
        print("SystemMonitor构造函数被调用...")

    def check_status(self, message):
        self.log(message)
        self.notify(message)

sys_monitor = SystemMonitor()
sys_monitor.check_status("设备运行良好")
print(SystemMonitor.__mro__)

image-20250708134643186

方法重写

习题3.1

创建一个 Shape 父类,包含一个 area() 方法,该方法打印“This is a generic shape.”。

○ 创建一个 Rectangle 子类,继承自 Shape。

○ 在 Rectangle 的 __init__ 方法中接收 width 和 height。

○ 重写 Rectangle 的 area() 方法,计算并返回矩形的面积。

○ 创建一个 Circle 子类,继承自 Shape。

○ 在 Circle 的 __init__ 方法中接收 radius。

○ 重写 Circle 的 area() 方法,计算并返回圆的面积(使用 PI = 3.14159)。

○ 创建 Shape、Rectangle 和 Circle 对象,并分别调用它们的 area() 方法。


参考:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
# 博客地址: https://kirsten-1.github.io/
# 创建者: kirsten-1
# 时间: 2025/7/8  14:11


PI = 3.14159
class Shape:
    def area(self):
        print("This is a generic shape.")

class Rectangle(Shape):
    def __init__(self, width, height):
        self.width = width
        self.height = height

    def area(self):
        return self.width * self.height


class Circle(Shape):
    def __init__(self, radius):
        self.radius = radius

    def area(self):
        return PI * self.radius

s = Shape()
s.area()
r = Rectangle(5, 8)
area_r = r.area()
print(f"Rectangle,长-{r.width}, 高-{r.height}, 面积是:{area_r}")
c = Circle(3)
area_c = c.area()
print(f"圆的半径是:{c.radius}, 面积是:{area_c}")

image-20250708141633645

习题3.2

创建一个 Employee 父类,包含 name 和 salary 属性,以及一个 calculate_bonus() 方法,该方法返回 salary * 0.10

○ 创建一个 Manager 子类,继承自 Employee。

○ 重写 Manager 的 calculate_bonus() 方法,使其返回 salary * 0.15

○ 创建 Employee 和 Manager 对象,并测试它们的奖金计算。


1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
# 博客地址: https://kirsten-1.github.io/
# 创建者: kirsten-1
# 时间: 2025/7/8  14:20

class Employee:
    def __init__(self, name, salary):
        self.name = name
        self.salary = salary
        print(f"员工-{self.name}被创建, 薪资是{self.salary}")

    def calculate_bonus(self):
        bonus = self.salary * 0.1
        print(f"基础奖金是:{bonus}")
        return bonus

class Manager(Employee):
    def __init__(self, name, saraly):
        super().__init__(name, saraly)

    def calculate_bonus(self):
        bonus = self.salary * 0.15
        print(f"基础奖金是:{bonus}")
        return bonus

e = Employee("Adele", 6000)
bonus_e = e.calculate_bonus()
print(f"员工:{e.name}的奖金是:{bonus_e}")

e2 = Manager("Bob", 6000)
bonus_e2 = e2.calculate_bonus()
print(f"员工:{e2.name}的奖金是:{bonus_e2}")

image-20250708142439557

Object

习题4.1

创建一个 Vector2D 类,表示二维向量,包含 x 和 y 两个属性。

○ 重写 __str__ 方法,使其打印出 “(x, y)” 的形式。

○ 重写 __repr__ 方法,使其返回一个可以用于重新创建对象的字符串,例如 Vector2D(x=1, y=2)

○ 重写 __eq__ 方法,定义两个向量在 x 和 y 都相等时才相等。

○ 重写 __add__ 方法,定义向量的加法(对应 + 运算符)

○ 创建 Vector2D 对象并测试这些重写的方法。


1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
# 博客地址: https://kirsten-1.github.io/
# 创建者: kirsten-1
# 时间: 2025/7/8  14:57

class Vector2D:
    def __init__(self, x, y):
        self.x = x
        self.y = y

    def __str__(self):
        return f"({self.x},{self.y})"

    def __repr__(self):
        # 返回一个可以用于重新创建对象的字符串
        return f"Vector2D(x={self.x}, y={self.y})"

    def __eq__(self, other):
        # 两个向量在 x 和 y 都相等时才相等
        if self.x == other.x and self.y == other.y:
            return True
        return  False

    def __add__(self, other):
        # 向量的加法(对应 + 运算符)
        self.x += other.x
        self.y += other.y

# 测试
v1 = Vector2D(2, 3)
v2 = Vector2D(10, 20)
v3 = Vector2D(2, 3)
print(v1)
print(repr(v2))
# 获取对象的 __repr__ 字符串表示
format2 = repr(v2)
# 使用 eval() 函数从字符串重新创建对象
try:
    vec2 = eval(format2)
    print(f"从字符串创建的新对象: {vec2}")

    # 4. 验证新对象和原始对象是否相等 (需要 __eq__ 方法)
    if v2 == vec2:
        print("验证成功: 新对象与原始对象相等!")
    else:
        print("验证失败: 新对象与原始对象不相等。")
except NameError as e:
    print(f"错误:无法执行 eval。确保 Vector2D 类在 eval 环境中可见。({e})")
except Exception as e:
    print(f"执行 eval 时发生意外错误: {e}")

print(f"v1 == v3:{v1.__eq__(v3)}")
v1.__add__(v2)
print(f"v1 + v2后的v1:{v1}")

image-20250708150802313

习题4.2

创建一个 Product 类,包含 id (产品ID) 和 name (产品名称) 属性。

○ 重写 __hash__ 方法,使得两个 Product 对象只要 id 相同,就被认为是相同的哈希值。

○ 重写 __eq__ 方法,使得两个 Product 对象只要 id 相同,就被认为是相等的。

○ 创建一个字典,使用 Product 对象作为键,并测试其行为。


1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
# 博客地址: https://kirsten-1.github.io/
# 创建者: kirsten-1
# 时间: 2025/7/8  15:16

class Product:
    def __init__(self, id, name):
        """
        id (产品ID) 和 name (产品名称) 属性
        """
        self.id = id
        self.name = name
        print(f"产品{self.id}-{self.name}被创建...")

    def __hash__(self):
        """
        两个 Product 对象只要 id 相同,就被认为是相同的哈希值
        """
        return hash(self.id)

    def __eq__(self, other):
        """
        两个 Product 对象只要 id 相同,就被认为是相等的。
        """
        return self.id == other.id

# 创建一个字典,使用 Product 对象作为键,并测试其行为
dict1 = {Product(1, "苹果"): "a", Product(1, "香蕉"): "b", Product(2, "西瓜"): "c", Product(3, "哈密瓜"): "d"}
print(len(dict1))

image-20250708152122558

多态

习题5.1

创建一个函数 process_vehicle(vehicle),它接受一个 vehicle 对象。

○ 如果 vehicle 对象有 drive() 方法,就调用它。

○ 如果 vehicle 对象有 sail() 方法,就调用它。

○ 如果 vehicle 对象有 fly() 方法,就调用它。

○ 创建 Car、Boat 和 Airplane 三个类,它们分别实现各自的 drive()、sail()、fly() 方法。

○ 创建 AmphibiousVehicle 类,同时实现 drive() 和 sail() 方法。

○ 创建这些类的对象,并使用 process_vehicle 函数处理它们。


1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
# 博客地址: https://kirsten-1.github.io/
# 创建者: kirsten-1
# 时间: 2025/7/8  15:53

# 创建 Car、Boat 和 Airplane 三个类,它们分别实现各自的 drive()、sail()、fly() 方法
class Car:
    def drive(self):
        print("汽车drive开一开...")

    def sail(self):
        print("汽车sail不了...")

    def fly(self):
        print("汽车fly不了...")

class Boat:
    def drive(self):
        print("船可以drive吗...")

    def sail(self):
        print("船勇敢sail...")

    def fly(self):
        print("船无法sail吧...")

class Airplane:
    def drive(self):
        print("飞机可以drive吗...")

    def sail(self):
        print("飞机不能sail吧...")

    def fly(self):
        print("飞机勇敢fly...")

# 创建 AmphibiousVehicle 类,同时实现 drive() 和 sail() 方法
class AmphibiousVehicle:
    def drive(self):
        print("AmphibiousVehicle的Drive方法被调用了...")

    def sail(self):
        print("AmphibiousVehicle的sail方法被调用了...")

def process_vehicle(vehicle):
    if hasattr(vehicle, "drive"):
        vehicle.drive()
    if hasattr(vehicle, "sail"):
        vehicle.sail()
    if hasattr(vehicle, "fly"):
        vehicle.fly()

car = Car()
boat = Boat()
ap = Airplane()
amphibiousVehicle = AmphibiousVehicle()
set1 = {car, boat, ap, amphibiousVehicle}
for i in set1:
    process_vehicle(i)

image-20250708160059304

习题 5.2 (抽象基类)

创建一个抽象基类 PaymentMethod,包含一个抽象方法 process_payment(amount)

○ 创建 CreditCardPayment 子类,实现 process_payment 方法,模拟信用卡支付逻辑。

○ 创建 PayPalPayment 子类,实现 process_payment 方法,模拟PayPal支付逻辑。

○ 创建一个函数 checkout(payment_method, total_amount),接收一个 PaymentMethod 类型的对象和一个金额,然后调用其 process_payment 方法。

○ 尝试实例化 PaymentMethod 类,观察是否报错。

○ 创建 CreditCardPayment 和 PayPalPayment 对象,并使用 checkout 函数进行支付。


1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
# 博客地址: https://kirsten-1.github.io/
# 创建者: kirsten-1
# 时间: 2025/7/8  16:01
import abc
class PaymentMethod(abc.ABC):
    @abc.abstractmethod
    def process_payment(self, amount):
        pass

class CreditCardPayment(PaymentMethod):
    def process_payment(self, amount):
        print(f"信用卡支付{amount}元")

class PayPalPayment(PaymentMethod):
    def process_payment(self, amount):
        print(f"Paypal支付{amount}元")

def checkout(payment_method, total_amount):
    payment_method.process_payment(total_amount)


try:
    pm = PaymentMethod()
except TypeError as e:
    print(f"报错:{e}")

ccp = CreditCardPayment()
ppp = PayPalPayment()
dict1 = {ccp: 200, ppp: 300}
for i in dict1.items():
    checkout(i[0], i[1])

image-20250708162504291