4、Python面向对象编程与模块化设计

发布于:2025-09-10 ⋅ 阅读:(23) ⋅ 点赞:(0)

学习目标:建立面向对象编程思维,掌握代码组织和模块化设计方法,培养大型项目开发能力

当然实际没人拿jupyter做开发(不是说Python不能开发),有这个思维即可。


4.1 类与对象:从现实世界到代码世界

> 什么是类和对象

想象一下,你想要描述"学生"这个概念。所有学生都有一些共同特征:姓名、年龄、学号,以及一些共同行为:上课、做作业、考试。类(Class) 就像一个模板或图纸,定义了学生应该有什么特征和行为。而对象(Object) 就是根据这个模板创建出来的具体的学生,比如"张三"、“李四”。

# 定义一个学生类 - 这是模板
class Student:
    def __init__(self, name, age, student_id):
        """初始化方法:创建学生对象时会自动调用"""
        self.name = name           # 学生姓名
        self.age = age             # 学生年龄  
        self.student_id = student_id  # 学号
        self.courses = []          # 选修的课程列表
    
    def introduce(self):
        """学生自我介绍的方法"""
        print(f"大家好,我是{self.name},今年{self.age}岁,学号是{self.student_id}")
    
    def enroll_course(self, course_name):
        """选修课程的方法"""
        self.courses.append(course_name)
        print(f"{self.name}成功选修了{course_name}课程")

# 创建具体的学生对象 - 根据模板制造具体的学生
student1 = Student("张三", 20, "2023001")
student2 = Student("李四", 19, "2023002")

# 使用对象的方法
student1.introduce()  # 大家好,我是张三,今年20岁,学号是2023001
student2.introduce()  # 大家好,我是李四,今年19岁,学号是2023002

# 调用对象的方法
student1.enroll_course("Python编程")
student2.enroll_course("数据结构")

# 访问对象的属性
print(f"{student1.name}选修的课程:{student1.courses}")

> 类和对象的核心概念

类和对象的关系可以这样理解:

类是抽象的概念:就像建筑图纸,定义了房子应该有什么房间、什么功能,但你不能住在图纸里。

对象是具体的实例:就像根据图纸建造的具体房子,每栋房子都有相同的结构,但可能有不同的装修和家具。

属性是对象的特征:就像房子的颜色、面积、房间数量等描述性信息。

方法是对象的行为:就像房子可以进行的操作:开门、关窗、打开灯等。

# 更详细的类定义示例
class Book:
    def __init__(self, title, author, pages, price):
        """书籍的基本信息"""
        self.title = title      # 书名
        self.author = author    # 作者
        self.pages = pages      # 页数
        self.price = price      # 价格
        self.is_borrowed = False  # 是否被借出
    
    def get_info(self):
        """获取书籍详细信息"""
        status = "已借出" if self.is_borrowed else "可借阅"
        return f"《{self.title}》- {self.author}著,{self.pages}页,价格¥{self.price},状态:{status}"
    
    def borrow(self):
        """借书方法"""
        if self.is_borrowed:
            return f"抱歉,《{self.title}》已经被借走了"
        else:
            self.is_borrowed = True
            return f"成功借阅《{self.title}》"
    
    def return_book(self):
        """还书方法"""
        if not self.is_borrowed:
            return f"《{self.title}》本来就在图书馆里"
        else:
            self.is_borrowed = False
            return f"成功归还《{self.title}》"

# 创建几本书
python_book = Book("Python编程", "张老师", 300, 59.9)
ai_book = Book("人工智能基础", "李教授", 450, 89.0)

# 查看书籍信息
print(python_book.get_info())
print(ai_book.get_info())

# 进行借书操作
print(python_book.borrow())  # 成功借阅
print(python_book.borrow())  # 已经被借走了

# 还书操作
print(python_book.return_book())  # 成功归还
print(python_book.get_info())     # 查看状态变化

4.2 封装:保护数据的安全

> 什么是封装

封装(Encapsulation) 就像给贵重物品装上保险箱。你可以决定哪些东西外人可以看到和使用,哪些东西需要保护起来。在编程中,封装让我们可以隐藏对象的内部细节,只暴露必要的接口。

class BankAccount:
    def __init__(self, account_number, initial_balance=0):
        self.account_number = account_number  # 账户号码(公开信息)
        self._balance = initial_balance       # 余额(受保护的信息,用_表示)
        self.__pin = "0000"                   # 密码(私有信息,用__表示)
        self.__transaction_history = []      # 交易记录(私有信息)
    
    def get_balance(self):
        """查询余额 - 提供安全的访问方式"""
        return f"当前余额:¥{self._balance:.2f}"
    
    def deposit(self, amount):
        """存钱"""
        if amount > 0:
            self._balance += amount
            self.__transaction_history.append(f"存入 ¥{amount:.2f}")
            return f"成功存入¥{amount:.2f}{self.get_balance()}"
        else:
            return "存款金额必须大于0"
    
    def withdraw(self, amount, pin):
        """取钱 - 需要密码验证"""
        if pin != self.__pin:
            return "密码错误"
        
        if amount > self._balance:
            return "余额不足"
        
        if amount > 0:
            self._balance -= amount
            self.__transaction_history.append(f"取出 ¥{amount:.2f}")
            return f"成功取出¥{amount:.2f}{self.get_balance()}"
        else:
            return "取款金额必须大于0"
    
    def change_pin(self, old_pin, new_pin):
        """修改密码"""
        if old_pin != self.__pin:
            return "旧密码错误"
        
        self.__pin = new_pin
        return "密码修改成功"
    
    def get_transaction_history(self, pin):
        """查询交易记录 - 需要密码验证"""
        if pin != self.__pin:
            return "密码错误,无法查询交易记录"
        
        if not self.__transaction_history:
            return "暂无交易记录"
        
        history = "交易记录:\n"
        for i, transaction in enumerate(self.__transaction_history, 1):
            history += f"{i}. {transaction}\n"
        return history

# 使用银行账户
my_account = BankAccount("123456789", 1000)

# 正常操作
print(my_account.get_balance())           # 查询余额
print(my_account.deposit(500))            # 存钱
print(my_account.withdraw(200, "0000"))   # 取钱

# 尝试非法操作
print(my_account.withdraw(300, "1234"))   # 密码错误
print(my_account.withdraw(2000, "0000"))  # 余额不足

# 查询交易记录
print(my_account.get_transaction_history("0000"))

# 直接访问私有属性会发生什么?
print(f"账户号:{my_account.account_number}")     # 可以访问
print(f"余额:{my_account._balance}")              # 可以访问但不推荐
# print(my_account.__pin)  # 这行会报错,因为是私有属性

在这里插入图片描述

> 属性装饰器:更优雅的封装方式

Python提供了更优雅的封装方式,使用属性装饰器可以让方法像属性一样使用:

class Temperature:
    def __init__(self, celsius=0):
        self._celsius = celsius
    
    @property
    def celsius(self):
        """获取摄氏温度"""
        return self._celsius
    
    @celsius.setter  
    def celsius(self, value):
        """设置摄氏温度,并进行合理性检查"""
        if value < -273.15:
            raise ValueError("温度不能低于绝对零度(-273.15°C)")
        self._celsius = value
    
    @property
    def fahrenheit(self):
        """自动计算华氏温度"""
        return self._celsius * 9/5 + 32
    
    @fahrenheit.setter
    def fahrenheit(self, value):
        """通过华氏温度设置摄氏温度"""
        self.celsius = (value - 32) * 5/9
    
    @property
    def kelvin(self):
        """自动计算开尔文温度"""
        return self._celsius + 273.15
    
    def __str__(self):
        """自定义对象的字符串表示"""
        return f"{self.celsius:.1f}°C ({self.fahrenheit:.1f}°F, {self.kelvin:.1f}K)"

# 使用温度类
temp = Temperature(25)
print(temp)  # 25.0°C (77.0°F, 298.1K)

# 像访问属性一样使用方法
temp.celsius = 30
print(f"摄氏度:{temp.celsius}°C")
print(f"华氏度:{temp.fahrenheit:.1f}°F")

# 通过华氏度设置温度
temp.fahrenheit = 100
print(temp)  # 37.8°C (100.0°F, 310.9K)

4.3 继承:代码复用的艺术

> 什么是继承

继承(Inheritance) 就像家族传承,孩子会继承父母的特征,但也会有自己独特的地方。在编程中,继承让新的类可以获得已有类的所有功能,然后在此基础上添加或修改功能。

# 基础类(父类)
class Person:
    def __init__(self, name, age):
        self.name = name
        self.age = age
    
    def introduce(self):
        print(f"我是{self.name},今年{self.age}岁")
    
    def sleep(self):
        print(f"{self.name}正在睡觉")
    
    def eat(self):
        print(f"{self.name}正在吃饭")

# 学生类(子类)- 继承自Person
class Student(Person):
    def __init__(self, name, age, student_id, major):
        super().__init__(name, age)  # 调用父类的初始化方法
        self.student_id = student_id  # 学生特有的属性
        self.major = major           # 专业
        self.courses = []            # 选修课程
    
    def introduce(self):
        """重写父类的方法,添加学生特有信息"""
        super().introduce()  # 调用父类的方法
        print(f"我是{self.major}专业的学生,学号是{self.student_id}")
    
    def study(self):
        """学生特有的方法"""
        print(f"{self.name}正在学习{self.major}")
    
    def take_exam(self):
        """学生特有的方法"""
        print(f"{self.name}正在参加考试")

# 教师类(子类)- 同样继承自Person
class Teacher(Person):
    def __init__(self, name, age, employee_id, subject):
        super().__init__(name, age)
        self.employee_id = employee_id
        self.subject = subject
        self.students = []
    
    def introduce(self):
        """重写父类的方法"""
        super().introduce()
        print(f"我是{self.subject}老师,工号是{self.employee_id}")
    
    def teach(self):
        """教师特有的方法"""
        print(f"{self.name}正在教授{self.subject}")
    
    def grade_paper(self):
        """教师特有的方法"""
        print(f"{self.name}正在批改试卷")

# 使用继承的类
student = Student("张三", 20, "2023001", "计算机科学")
teacher = Teacher("李老师", 35, "T001", "Python编程")

# 学生和老师都有父类的方法
student.introduce()  # 包含学生特有信息的自我介绍
teacher.introduce()  # 包含教师特有信息的自我介绍

student.eat()        # 继承自Person的方法
teacher.sleep()      # 继承自Person的方法

# 各自的特有方法
student.study()      # 学生特有
student.take_exam()  # 学生特有
teacher.teach()      # 教师特有
teacher.grade_paper() # 教师特有

> 多层继承和方法重写

继承可以有多个层次,子类还可以有自己的子类:

# 更深层次的继承示例
class Animal:
    def __init__(self, name, species):
        self.name = name
        self.species = species
    
    def make_sound(self):
        print(f"{self.name}发出了声音")
    
    def move(self):
        print(f"{self.name}正在移动")

class Mammal(Animal):  # 哺乳动物
    def __init__(self, name, species, fur_color):
        super().__init__(name, species)
        self.fur_color = fur_color
    
    def feed_milk(self):
        print(f"{self.name}正在哺乳")

class Dog(Mammal):     # 狗继承自哺乳动物
    def __init__(self, name, breed, fur_color="棕色"):
        super().__init__(name, "犬科", fur_color)
        self.breed = breed
    
    def make_sound(self):  # 重写祖父类的方法
        print(f"{self.name}汪汪叫")
    
    def fetch(self):       # 狗特有的方法
        print(f"{self.name}去捡球了")

class Cat(Mammal):     # 猫继承自哺乳动物
    def __init__(self, name, fur_color="白色"):
        super().__init__(name, "猫科", fur_color)
    
    def make_sound(self):  # 重写祖父类的方法
        print(f"{self.name}喵喵叫")
    
    def climb(self):       # 猫特有的方法
        print(f"{self.name}爬到了树上")

# 创建动物对象
my_dog = Dog("旺财", "金毛", "金色")
my_cat = Cat("小咪", "黑色")

# 展示继承链的方法调用
print("=== 狗的行为 ===")
my_dog.make_sound()  # 狗重写的方法
my_dog.move()        # 从Animal继承的方法
my_dog.feed_milk()   # 从Mammal继承的方法
my_dog.fetch()       # 狗特有的方法

print("\n=== 猫的行为 ===")
my_cat.make_sound()  # 猫重写的方法
my_cat.move()        # 从Animal继承的方法
my_cat.feed_milk()   # 从Mammal继承的方法
my_cat.climb()       # 猫特有的方法

# 查看继承关系
print(f"\n{my_dog.name}{my_dog.species},品种是{my_dog.breed}")
print(f"{my_cat.name}{my_cat.species},毛色是{my_cat.fur_color}")

4.4 异常处理:让程序更健壮

> 什么是异常处理

异常处理(Exception Handling) 就像给程序装上安全带。当程序遇到意外情况时,不会直接崩溃,而是有计划地处理这些问题,保证程序能够优雅地应对各种错误情况。

# 基本的异常处理结构
def safe_divide(a, b):
    """安全的除法函数"""
    try:
        # 尝试执行可能出错的代码
        result = a / b
        return f"{a} ÷ {b} = {result}"
    except ZeroDivisionError:
        # 处理除零错误
        return "错误:不能除以零"
    except TypeError:
        # 处理类型错误
        return "错误:请输入数字"
    except Exception as e:
        # 处理其他所有异常
        return f"发生未知错误:{e}"
    finally:
        # 无论是否出错都会执行的代码
        print("除法运算尝试完成")

# 测试异常处理
print(safe_divide(10, 2))      # 正常情况
print(safe_divide(10, 0))      # 除零错误
print(safe_divide("10", 2))    # 类型错误

> 自定义异常类

我们可以创建自己的异常类,让错误信息更有意义:

# 自定义异常类
class InsufficientBalanceError(Exception):
    """余额不足异常"""
    def __init__(self, current_balance, required_amount):
        self.current_balance = current_balance
        self.required_amount = required_amount
        super().__init__(f"余额不足:当前余额¥{current_balance},需要¥{required_amount}")

class InvalidAmountError(Exception):
    """无效金额异常"""
    def __init__(self, amount):
        self.amount = amount
        super().__init__(f"无效金额:¥{amount},金额必须大于0")

class AccountLockedError(Exception):
    """账户锁定异常"""
    def __init__(self, reason):
        self.reason = reason
        super().__init__(f"账户已锁定:{reason}")

# 使用自定义异常的银行账户类
class SafeBankAccount:
    def __init__(self, account_number, initial_balance=0):
        self.account_number = account_number
        self._balance = initial_balance
        self._is_locked = False
        self._failed_attempts = 0
    
    def withdraw(self, amount):
        """取钱方法,包含完整的异常处理"""
        try:
            # 检查账户状态
            if self._is_locked:
                raise AccountLockedError("连续输错密码超过3次")
            
            # 检查金额有效性
            if amount <= 0:
                raise InvalidAmountError(amount)
            
            # 检查余额
            if amount > self._balance:
                raise InsufficientBalanceError(self._balance, amount)
            
            # 执行取款
            self._balance -= amount
            return f"取款成功!取出¥{amount:.2f},余额¥{self._balance:.2f}"
            
        except (InsufficientBalanceError, InvalidAmountError, AccountLockedError) as e:
            # 记录错误日志
            print(f"取款失败:{e}")
            return str(e)
        except Exception as e:
            # 处理意外错误
            print(f"系统错误:{e}")
            return "取款失败,请联系客服"
    
    def verify_pin(self, pin):
        """验证密码"""
        correct_pin = "1234"  # 简化示例
        
        if pin == correct_pin:
            self._failed_attempts = 0
            return True
        else:
            self._failed_attempts += 1
            if self._failed_attempts >= 3:
                self._is_locked = True
            return False

# 使用带异常处理的账户
account = SafeBankAccount("987654321", 1000)

# 测试各种情况
print("=== 测试取款功能 ===")
print(account.withdraw(500))     # 正常取款
print(account.withdraw(-100))    # 无效金额
print(account.withdraw(2000))    # 余额不足

# 测试账户锁定
print("\n=== 测试密码验证 ===")
for i in range(4):
    result = account.verify_pin("wrong")
    print(f"第{i+1}次密码错误")

print(account.withdraw(100))     # 账户锁定后尝试取款

> 异常处理的最佳实践

import logging
from datetime import datetime

# 配置日志
logging.basicConfig(level=logging.INFO, format='%(asctime)s - %(levelname)s - %(message)s')

class DataProcessor:
    def __init__(self):
        self.processed_count = 0
        self.error_count = 0
    
    def process_file(self, filename):
        """处理文件的完整异常处理示例"""
        try:
            logging.info(f"开始处理文件:{filename}")
            
            # 尝试打开文件
            with open(filename, 'r', encoding='utf-8') as file:
                lines = file.readlines()
            
            # 处理每一行数据
            results = []
            for line_num, line in enumerate(lines, 1):
                try:
                    # 处理单行数据
                    processed_data = self._process_line(line.strip())
                    results.append(processed_data)
                    self.processed_count += 1
                
                except ValueError as e:
                    # 单行处理错误,记录但继续处理其他行
                    logging.warning(f"第{line_num}行数据格式错误:{e}")
                    self.error_count += 1
                    continue
                
                except Exception as e:
                    # 其他未预期错误
                    logging.error(f"第{line_num}行处理时发生未知错误:{e}")
                    self.error_count += 1
                    continue
            
            logging.info(f"文件处理完成:成功{self.processed_count}行,错误{self.error_count}行")
            return results
        
        except FileNotFoundError:
            error_msg = f"文件不存在:{filename}"
            logging.error(error_msg)
            raise FileNotFoundError(error_msg)
        
        except PermissionError:
            error_msg = f"没有权限访问文件:{filename}"
            logging.error(error_msg)
            raise PermissionError(error_msg)
        
        except UnicodeDecodeError:
            error_msg = f"文件编码错误,无法解析:{filename}"
            logging.error(error_msg)
            raise UnicodeDecodeError("utf-8", b"", 0, 1, error_msg)
        
        except Exception as e:
            error_msg = f"处理文件时发生未知错误:{e}"
            logging.error(error_msg)
            raise Exception(error_msg)
    
    def _process_line(self, line):
        """处理单行数据"""
        if not line:
            raise ValueError("空行")
        
        # 假设每行格式为:姓名,年龄,分数
        parts = line.split(',')
        if len(parts) != 3:
            raise ValueError(f"数据格式错误,应为3列,实际为{len(parts)}列")
        
        name = parts[0].strip()
        try:
            age = int(parts[1].strip())
            score = float(parts[2].strip())
        except ValueError:
            raise ValueError("年龄或分数格式错误")
        
        if age < 0 or age > 150:
            raise ValueError(f"年龄不合理:{age}")
        
        if score < 0 or score > 100:
            raise ValueError(f"分数不合理:{score}")
        
        return {"name": name, "age": age, "score": score}

# 创建示例数据文件用于测试
sample_data = """张三,20,85.5
李四,22,92.0
王五,abc,78.5
赵六,25,105.0
钱七,19,88.0
,21,90.0
孙八,23,95.5"""

with open("test_data.txt", "w", encoding="utf-8") as f:
    f.write(sample_data)

# 使用异常处理
processor = DataProcessor()
try:
    results = processor.process_file("test_data.txt")
    print(f"\n成功处理的数据:")
    for data in results:
        print(f"  {data['name']}: {data['age']}岁,分数{data['score']}")
        
except Exception as e:
    print(f"处理失败:{e}")

4.5 模块与包:代码组织的艺术

> 什么是模块和包

模块(Module) 就像一个工具箱,里面放着相关的函数和类。包(Package) 就像一个工具仓库,里面有很多个工具箱。通过模块和包,我们可以把代码按功能组织起来,方便管理和复用。

# 首先创建math_utils.py模块文件
math_utils_content = '''"""
数学工具模块
提供常用的数学计算功能
"""

import math

def calculate_circle_area(radius):
    """计算圆形面积"""
    if radius < 0:
        raise ValueError("半径不能为负数")
    return math.pi * radius ** 2

def calculate_rectangle_area(length, width):
    """计算矩形面积"""
    if length < 0 or width < 0:
        raise ValueError("长度和宽度不能为负数")
    return length * width

def is_prime(n):
    """判断是否为质数"""
    if n < 2:
        return False
    for i in range(2, int(math.sqrt(n)) + 1):
        if n % i == 0:
            return False
    return True

def factorial(n):
    """计算阶乘"""
    if n < 0:
        raise ValueError("负数没有阶乘")
    if n == 0 or n == 1:
        return 1
    return n * factorial(n - 1)

# 模块级变量
PI = math.pi
E = math.e

if __name__ == "__main__":
    print("数学工具模块测试")
    print(f"圆形面积(半径5):{calculate_circle_area(5)}")
    print(f"矩形面积(3x4):{calculate_rectangle_area(3, 4)}")
    print(f"7是质数吗?{is_prime(7)}")
    print(f"5的阶乘:{factorial(5)}")
'''

# 创建math_utils.py文件
with open('math_utils.py', 'w', encoding='utf-8') as f:
    f.write(math_utils_content)

print("math_utils.py模块文件已创建")

# 现在可以正常导入和使用模块了
import importlib
import sys

# 如果模块已经导入过,需要重新加载
if 'math_utils' in sys.modules:
    importlib.reload(sys.modules['math_utils'])

# 不同的导入方式

# 方式1:导入整个模块
import math_utils
result1 = math_utils.calculate_circle_area(5)
print(f"方式1结果:{result1}")

# 方式2:导入特定函数
from math_utils import calculate_circle_area, is_prime
result2 = calculate_circle_area(3)
prime_check = is_prime(17)
print(f"方式2结果:面积={result2},17是质数={prime_check}")

# 方式3:导入所有公开的内容(不推荐在大项目中使用)
from math_utils import *
result3 = calculate_rectangle_area(4, 6)
print(f"方式3结果:{result3}")

# 方式4:使用别名
import math_utils as mu
result4 = mu.factorial(4)
print(f"方式4结果:{result4}")

# 方式5:为导入的函数起别名
from math_utils import calculate_circle_area as circle_area
result5 = circle_area(7)
print(f"方式5结果:{result5}")

> 创建包结构

包是包含多个模块的目录。让我们创建一个完整的包结构:

# 创建包目录结构(用代码模拟文件创建过程)
import os

# 创建包目录
package_dir = "school_management"
if not os.path.exists(package_dir):
    os.makedirs(package_dir)

# 创建子包目录
subpackages = ["students", "teachers", "courses"]
for sub in subpackages:
    subdir = os.path.join(package_dir, sub)
    if not os.path.exists(subdir):
        os.makedirs(subdir)

# 创建 __init__.py 文件(让目录成为Python包)
def create_init_file(path, content=""):
    with open(os.path.join(path, "__init__.py"), "w", encoding="utf-8") as f:
        f.write(content)

# 主包的 __init__.py
main_init_content = '''"""
学校管理系统包
"""

from .students import student_manager
from .teachers import teacher_manager  
from .courses import course_manager

__version__ = "1.0.0"
__author__ = "Python学习者"

print("学校管理系统已加载")
'''
create_init_file(package_dir, main_init_content)

# students子包的模块
student_module_content = '''"""
学生管理模块
"""

class Student:
    def __init__(self, name, student_id, grade):
        self.name = name
        self.student_id = student_id
        self.grade = grade
        self.courses = []
    
    def enroll_course(self, course):
        if course not in self.courses:
            self.courses.append(course)
            return f"{self.name}成功选修{course}"
        return f"{self.name}已经选修了{course}"
    
    def __str__(self):
        return f"学生:{self.name}(学号:{self.student_id},年级:{self.grade})"

class StudentManager:
    def __init__(self):
        self.students = {}
    
    def add_student(self, name, student_id, grade):
        if student_id in self.students:
            return f"学号{student_id}已存在"
        
        student = Student(name, student_id, grade)
        self.students[student_id] = student
        return f"成功添加学生:{name}"
    
    def get_student(self, student_id):
        return self.students.get(student_id)
    
    def list_students(self):
        return list(self.students.values())

# 创建全局学生管理器
student_manager = StudentManager()
'''

with open(os.path.join(package_dir, "students", "student_manager.py"), "w", encoding="utf-8") as f:
    f.write(student_module_content)

# students子包的 __init__.py
students_init_content = '''"""
学生管理子包
"""

from .student_manager import Student, StudentManager, student_manager

__all__ = ["Student", "StudentManager", "student_manager"]
'''
create_init_file(os.path.join(package_dir, "students"), students_init_content)

print("包结构创建完成!")
print("目录结构:")
for root, dirs, files in os.walk(package_dir):
    level = root.replace(package_dir, '').count(os.sep)
    indent = ' ' * 2 * level
    print(f"{indent}{os.path.basename(root)}/")
    subindent = ' ' * 2 * (level + 1)
    for file in files:
        print(f"{subindent}{file}")

4.6 实战项目:学生选课管理系统

现在让我们把所有学到的知识综合起来,构建一个完整的学生选课管理系统:

# course_system.py - 完整的选课管理系统

"""
学生选课管理系统
整合面向对象编程、继承、异常处理和模块化设计
"""

import json
import os
from datetime import datetime
from abc import ABC, abstractmethod

# 自定义异常类
class CourseSystemError(Exception):
    """课程系统基础异常类"""
    pass

class StudentNotFoundError(CourseSystemError):
    """学生未找到异常"""
    pass

class CourseNotFoundError(CourseSystemError):
    """课程未找到异常"""
    pass

class CourseFullError(CourseSystemError):
    """课程已满异常"""
    pass

class AlreadyEnrolledError(CourseSystemError):
    """已经选课异常"""
    pass

# 基础用户类
class User(ABC):
    """用户抽象基类"""
    def __init__(self, user_id, name, email):
        self.user_id = user_id
        self.name = name
        self.email = email
        self.created_at = datetime.now()
    
    @abstractmethod
    def get_info(self):
        """获取用户信息的抽象方法"""
        pass
    
    def __str__(self):
        return f"{self.__class__.__name__}: {self.name} ({self.user_id})"

# 学生类
class Student(User):
    def __init__(self, student_id, name, email, major, year):
        super().__init__(student_id, name, email)
        self.major = major
        self.year = year
        self.enrolled_courses = []
        self.completed_courses = []
        self.gpa = 0.0
    
    def get_info(self):
        """获取学生详细信息"""
        return {
            "student_id": self.user_id,
            "name": self.name,
            "email": self.email,
            "major": self.major,
            "year": self.year,
            "enrolled_courses": len(self.enrolled_courses),
            "completed_courses": len(self.completed_courses),
            "gpa": self.gpa,
            "created_at": self.created_at.isoformat()
        }
    
    def enroll_course(self, course):
        """选修课程"""
        if course in self.enrolled_courses:
            raise AlreadyEnrolledError(f"学生{self.name}已经选修了课程{course.course_name}")
        
        if len(course.enrolled_students) >= course.max_students:
            raise CourseFullError(f"课程{course.course_name}已满员")
        
        self.enrolled_courses.append(course)
        course.add_student(self)
        return f"学生{self.name}成功选修课程{course.course_name}"
    
    def drop_course(self, course):
        """退选课程"""
        if course not in self.enrolled_courses:
            raise CourseNotFoundError(f"学生{self.name}未选修课程{course.course_name}")
        
        self.enrolled_courses.remove(course)
        course.remove_student(self)
        return f"学生{self.name}成功退选课程{course.course_name}"

# 教师类
class Teacher(User):
    def __init__(self, teacher_id, name, email, department, title):
        super().__init__(teacher_id, name, email)
        self.department = department
        self.title = title
        self.teaching_courses = []
    
    def get_info(self):
        """获取教师详细信息"""
        return {
            "teacher_id": self.user_id,
            "name": self.name,
            "email": self.email,
            "department": self.department,
            "title": self.title,
            "teaching_courses": len(self.teaching_courses),
            "created_at": self.created_at.isoformat()
        }
    
    def assign_course(self, course):
        """分配课程"""
        if course not in self.teaching_courses:
            self.teaching_courses.append(course)
            course.teacher = self

# 课程类
class Course:
    def __init__(self, course_id, course_name, credits, max_students=30):
        self.course_id = course_id
        self.course_name = course_name
        self.credits = credits
        self.max_students = max_students
        self.enrolled_students = []
        self.teacher = None
        self.created_at = datetime.now()
    
    def get_info(self):
        """获取课程详细信息"""
        return {
            "course_id": self.course_id,
            "course_name": self.course_name,
            "credits": self.credits,
            "max_students": self.max_students,
            "current_enrollment": len(self.enrolled_students),
            "available_spots": self.max_students - len(self.enrolled_students),
            "teacher": self.teacher.name if self.teacher else "未分配",
            "created_at": self.created_at.isoformat()
        }
    
    def add_student(self, student):
        """添加学生到课程"""
        if student not in self.enrolled_students:
            self.enrolled_students.append(student)
    
    def remove_student(self, student):
        """从课程中移除学生"""
        if student in self.enrolled_students:
            self.enrolled_students.remove(student)
    
    def __str__(self):
        return f"课程:{self.course_name} ({self.course_id}) - {self.credits}学分"

# 选课管理系统
class CourseManagementSystem:
    def __init__(self):
        self.students = {}
        self.teachers = {}
        self.courses = {}
        self.data_file = "course_system_data.json"
    
    def add_student(self, student_id, name, email, major, year):
        """添加学生"""
        try:
            if student_id in self.students:
                raise ValueError(f"学号{student_id}已存在")
            
            student = Student(student_id, name, email, major, year)
            self.students[student_id] = student
            return f"成功添加学生:{name}"
            
        except Exception as e:
            raise CourseSystemError(f"添加学生失败:{e}")
    
    def add_teacher(self, teacher_id, name, email, department, title):
        """添加教师"""
        try:
            if teacher_id in self.teachers:
                raise ValueError(f"工号{teacher_id}已存在")
            
            teacher = Teacher(teacher_id, name, email, department, title)
            self.teachers[teacher_id] = teacher
            return f"成功添加教师:{name}"
            
        except Exception as e:
            raise CourseSystemError(f"添加教师失败:{e}")
    
    def add_course(self, course_id, course_name, credits, max_students=30):
        """添加课程"""
        try:
            if course_id in self.courses:
                raise ValueError(f"课程编号{course_id}已存在")
            
            course = Course(course_id, course_name, credits, max_students)
            self.courses[course_id] = course
            return f"成功添加课程:{course_name}"
            
        except Exception as e:
            raise CourseSystemError(f"添加课程失败:{e}")
    
    def enroll_student(self, student_id, course_id):
        """学生选课"""
        try:
            student = self.students.get(student_id)
            if not student:
                raise StudentNotFoundError(f"未找到学号为{student_id}的学生")
            
            course = self.courses.get(course_id)
            if not course:
                raise CourseNotFoundError(f"未找到课程编号为{course_id}的课程")
            
            return student.enroll_course(course)
            
        except (StudentNotFoundError, CourseNotFoundError, AlreadyEnrolledError, CourseFullError):
            raise
        except Exception as e:
            raise CourseSystemError(f"选课失败:{e}")
    
    def assign_teacher(self, teacher_id, course_id):
        """为课程分配教师"""
        try:
            teacher = self.teachers.get(teacher_id)
            if not teacher:
                raise ValueError(f"未找到工号为{teacher_id}的教师")
            
            course = self.courses.get(course_id)
            if not course:
                raise CourseNotFoundError(f"未找到课程编号为{course_id}的课程")
            
            teacher.assign_course(course)
            return f"成功为课程{course.course_name}分配教师{teacher.name}"
            
        except Exception as e:
            raise CourseSystemError(f"分配教师失败:{e}")
    
    def get_system_statistics(self):
        """获取系统统计信息"""
        return {
            "total_students": len(self.students),
            "total_teachers": len(self.teachers),
            "total_courses": len(self.courses),
            "active_enrollments": sum(len(student.enrolled_courses) for student in self.students.values()),
            "courses_with_teachers": sum(1 for course in self.courses.values() if course.teacher),
            "average_class_size": sum(len(course.enrolled_students) for course in self.courses.values()) / max(len(self.courses), 1)
        }
    
    def save_data(self):
        """保存数据到文件"""
        try:
            data = {
                "students": {sid: student.get_info() for sid, student in self.students.items()},
                "teachers": {tid: teacher.get_info() for tid, teacher in self.teachers.items()},
                "courses": {cid: course.get_info() for cid, course in self.courses.items()},
                "last_updated": datetime.now().isoformat()
            }
            
            with open(self.data_file, 'w', encoding='utf-8') as f:
                json.dump(data, f, ensure_ascii=False, indent=2)
            
            return f"数据已保存到{self.data_file}"
            
        except Exception as e:
            raise CourseSystemError(f"保存数据失败:{e}")
    
    def generate_report(self):
        """生成系统报告"""
        stats = self.get_system_statistics()
        
        report = f"""
=== 选课管理系统报告 ===
生成时间:{datetime.now().strftime('%Y-%m-%d %H:%M:%S')}

基本统计:
- 学生总数:{stats['total_students']}
- 教师总数:{stats['total_teachers']}  
- 课程总数:{stats['total_courses']}
- 选课总数:{stats['active_enrollments']}
- 已分配教师的课程:{stats['courses_with_teachers']}
- 平均班级大小:{stats['average_class_size']:.1f}人

课程详情:
"""
        for course in self.courses.values():
            info = course.get_info()
            report += f"- {info['course_name']}{info['current_enrollment']}/{info['max_students']}人"
            if info['teacher'] != "未分配":
                report += f"(教师:{info['teacher']})"
            report += "\n"
        
        return report

# 系统演示和测试
def demonstrate_course_system():
    """演示选课管理系统的完整功能"""
    
    print("=== 选课管理系统演示 ===")
    
    # 创建系统实例
    cms = CourseManagementSystem()
    
    try:
        # 添加教师
        print("\n1. 添加教师")
        print(cms.add_teacher("T001", "张教授", "zhang@university.edu", "计算机科学", "教授"))
        print(cms.add_teacher("T002", "李老师", "li@university.edu", "数学系", "副教授"))
        
        # 添加课程
        print("\n2. 添加课程")
        print(cms.add_course("CS101", "Python编程基础", 3, 25))
        print(cms.add_course("MATH201", "高等数学", 4, 30))
        print(cms.add_course("CS201", "数据结构", 3, 20))
        
        # 分配教师
        print("\n3. 分配教师")
        print(cms.assign_teacher("T001", "CS101"))
        print(cms.assign_teacher("T001", "CS201"))
        print(cms.assign_teacher("T002", "MATH201"))
        
        # 添加学生
        print("\n4. 添加学生")
        students_data = [
            ("S001", "张三", "zhangsan@student.edu", "计算机科学", 2023),
            ("S002", "李四", "lisi@student.edu", "计算机科学", 2023),
            ("S003", "王五", "wangwu@student.edu", "数学", 2022),
            ("S004", "赵六", "zhaoliu@student.edu", "计算机科学", 2024)
        ]
        
        for student_data in students_data:
            print(cms.add_student(*student_data))
        
        # 学生选课
        print("\n5. 学生选课")
        enrollments = [
            ("S001", "CS101"),
            ("S001", "MATH201"),
            ("S002", "CS101"),
            ("S002", "CS201"),
            ("S003", "MATH201"),
            ("S004", "CS101")
        ]
        
        for student_id, course_id in enrollments:
            try:
                print(cms.enroll_student(student_id, course_id))
            except CourseSystemError as e:
                print(f"选课失败:{e}")
        
        # 尝试异常情况
        print("\n6. 异常情况测试")
        try:
            print(cms.enroll_student("S001", "CS101"))  # 重复选课
        except AlreadyEnrolledError as e:
            print(f"预期的异常:{e}")
        
        try:
            print(cms.enroll_student("S999", "CS101"))  # 学生不存在
        except StudentNotFoundError as e:
            print(f"预期的异常:{e}")
        
        # 生成报告
        print("\n7. 系统报告")
        print(cms.generate_report())
        
        # 保存数据
        print("8. 保存数据")
        print(cms.save_data())
        
    except CourseSystemError as e:
        print(f"系统错误:{e}")
    except Exception as e:
        print(f"未预期的错误:{e}")
    
    return cms

# 运行演示
course_system = demonstrate_course_system()

> 项目技术要点总结

这个学生选课管理系统完整展示了面向对象编程和模块化设计的核心概念:

类设计与继承体系:通过User抽象基类建立用户层次结构,Student和Teacher类继承并扩展基础功能,体现了继承的代码复用价值。

封装与数据保护:合理使用私有属性和公共接口,通过方法控制数据访问,确保系统数据的一致性和安全性。

异常处理架构:设计了完整的异常类层次结构,从基础异常到具体业务异常,提供了精确的错误信息和处理机制。

模块化系统设计:各个类职责清晰,系统管理类协调各组件交互,体现了良好的软件架构设计原则。

数据持久化与报告生成:实现了数据的保存和加载功能,以及系统状态的报告生成,展现了实际软件系统的完整性。


4.7 学习总结与进阶指导

> 核心概念掌握检查

通过本课学习,我们建立了完整的面向对象编程思维体系。类和对象让我们能够模拟现实世界的概念,封装保护了数据的安全性,继承实现了代码的高效复用,异常处理提升了程序的健壮性,模块化设计让大型项目的开发和维护变得可行。

这些概念不仅是Python编程的高级特性,更是现代软件开发的基础思维模式。掌握了这些内容,我们就具备了开发复杂应用系统的基本能力。

> 实践建议与扩展方向

建议通过以下方式巩固学习成果:设计并实现自己的类层次结构,练习异常处理的各种场景,尝试创建可复用的模块和包。重点培养面向对象的设计思维和代码组织能力。

在大模型开发的背景下,这些技能将直接应用于模型架构设计、数据处理流程构建、训练系统开发等核心环节。面向对象的思维方式将帮助我们更好地理解和设计复杂的AI系统架构。

> 与后续课程的连接

本课内容为后续的科学计算和数据处理课程奠定了重要基础。NumPy和Pandas等库的深度使用需要良好的面向对象编程基础,模块化设计思维将帮助我们更好地组织数据科学项目的代码结构。


附录:专业术语表

类(Class):定义对象属性和方法的模板,是面向对象编程的基本构建单元

对象(Object):根据类定义创建的具体实例,包含实际的数据和可执行的方法

封装(Encapsulation):将数据和操作数据的方法组合在一起,隐藏内部实现细节的编程原则

继承(Inheritance):子类获得父类属性和方法的机制,实现代码复用和层次化设计

多态(Polymorphism):同一接口在不同类中具有不同实现的特性,提高代码的灵活性

异常处理(Exception Handling):程序运行时处理错误和异常情况的机制,提高程序健壮性

模块(Module):包含Python定义和语句的文件,用于代码的组织和复用

包(Package):包含多个模块的目录结构,提供命名空间和模块层次化组织

抽象基类(Abstract Base Class):定义接口规范但不能直接实例化的类,用于建立类层次结构

属性装饰器(Property Decorator):将方法转换为属性访问形式的Python特性,提供更优雅的封装方式


网站公告

今日签到

点亮在社区的每一天
去签到