这是我大二上学期的面向对象入门记录。它不单独作为项目统计,主要价值在于从“写一个函数解题”过渡到“用对象组织数据和行为”。下面的题目与代码按课程方向整理为复盘版,适合之后重新运行和改造。

返回项目实践 · 001 高级语言程序设计 · 003 数据结构与算法课程设计

1、课程信息

项目 内容
学习阶段 大二上学期
主要语言 Python
课程定位 函数、类与对象、封装、模块拆分
关键词 组合计算、类设计、菜单交互、数据管理

2、题目与训练点

编号 题目 主要训练内容 现在回看
A02 阶乘和组合 函数拆分、循环或递归、结果验证 适合练习把公式拆成可复用函数,并处理输入范围
B03 学生课程系统 类与对象、数据管理、菜单交互 开始接近小型管理系统,重点是对象职责和状态维护

3、A02 阶乘和组合

3.1 题目描述

输入 nr,分别计算:

1
2
3
n!
A(n, r) = n! / (n-r)!
C(n, r) = n! / (r! × (n-r)!)

要求:

  1. nr 都是非负整数。
  2. r 不能大于 n
  3. 用函数拆分阶乘、排列和组合。

3.2 输入输出

输入:

1
5 2

输出:

1
2
3
5! = 120
A(5, 2) = 20
C(5, 2) = 10

3.3 完整代码

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
def factorial(n: int) -> int:
"""Return n!."""
result = 1
for value in range(2, n + 1):
result *= value
return result


def permutation(n: int, r: int) -> int:
"""Return A(n, r)."""
result = 1
for value in range(n - r + 1, n + 1):
result *= value
return result


def combination(n: int, r: int) -> int:
"""Return C(n, r)."""
r = min(r, n - r)
numerator = permutation(n, r)
denominator = factorial(r)
return numerator // denominator


def validate(n: int, r: int) -> bool:
if n < 0 or r < 0:
print("n and r must be non-negative integers.")
return False
if r > n:
print("r must not be greater than n.")
return False
return True


def main() -> None:
n, r = map(int, input().split())

if not validate(n, r):
return

print(f"{n}! = {factorial(n)}")
print(f"A({n}, {r}) = {permutation(n, r)}")
print(f"C({n}, {r}) = {combination(n, r)}")


if __name__ == "__main__":
main()

3.4 复盘点

这个题可以直接用 math.factorial,但课程练习阶段更适合自己写循环。组合数计算时使用 r = min(r, n-r),可以减少乘法次数,也能顺手理解组合公式的对称性。

4、B03 学生课程系统

4.1 题目描述

设计一个简单的学生课程管理系统,支持:

  1. 添加学生。
  2. 添加课程。
  3. 学生选课。
  4. 录入成绩。
  5. 查询学生成绩单。
  6. 查询课程名单。

系统只使用内存保存数据,不连接数据库。这个版本的重点是类与对象、封装、字典管理和菜单交互。

4.2 类设计

作用
Student 保存学生学号和姓名
Course 保存课程编号、课程名和学分
Enrollment 保存某个学生在某门课中的成绩
CourseSystem 负责添加、选课、录入成绩和查询

4.3 完整代码

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
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
from dataclasses import dataclass


@dataclass
class Student:
student_id: str
name: str


@dataclass
class Course:
course_id: str
name: str
credit: float


@dataclass
class Enrollment:
student_id: str
course_id: str
score: float | None = None


class CourseSystem:
def __init__(self) -> None:
self.students: dict[str, Student] = {}
self.courses: dict[str, Course] = {}
self.enrollments: dict[tuple[str, str], Enrollment] = {}

def add_student(self, student_id: str, name: str) -> None:
if student_id in self.students:
print("Student already exists.")
return
self.students[student_id] = Student(student_id, name)
print("Student added.")

def add_course(self, course_id: str, name: str, credit: float) -> None:
if course_id in self.courses:
print("Course already exists.")
return
self.courses[course_id] = Course(course_id, name, credit)
print("Course added.")

def enroll(self, student_id: str, course_id: str) -> None:
if student_id not in self.students:
print("Student not found.")
return
if course_id not in self.courses:
print("Course not found.")
return

key = (student_id, course_id)
if key in self.enrollments:
print("This student has already selected the course.")
return

self.enrollments[key] = Enrollment(student_id, course_id)
print("Enrollment saved.")

def set_score(self, student_id: str, course_id: str, score: float) -> None:
key = (student_id, course_id)
if key not in self.enrollments:
print("Enrollment not found.")
return
if not 0 <= score <= 100:
print("Score must be between 0 and 100.")
return

self.enrollments[key].score = score
print("Score updated.")

def print_student_report(self, student_id: str) -> None:
if student_id not in self.students:
print("Student not found.")
return

student = self.students[student_id]
print(f"\nTranscript: {student.student_id} {student.name}")
print("-" * 50)
print(f"{'Course ID':<12}{'Course':<18}{'Credit':<8}{'Score':<8}")

total_credit = 0.0
total_weighted_score = 0.0
has_score = False

for enrollment in self.enrollments.values():
if enrollment.student_id != student_id:
continue

course = self.courses[enrollment.course_id]
score_text = "N/A" if enrollment.score is None else f"{enrollment.score:.1f}"
print(f"{course.course_id:<12}{course.name:<18}{course.credit:<8.1f}{score_text:<8}")

if enrollment.score is not None:
has_score = True
total_credit += course.credit
total_weighted_score += course.credit * enrollment.score

if has_score and total_credit > 0:
average = total_weighted_score / total_credit
print("-" * 50)
print(f"Weighted average: {average:.2f}")
print()

def print_course_roster(self, course_id: str) -> None:
if course_id not in self.courses:
print("Course not found.")
return

course = self.courses[course_id]
print(f"\nRoster: {course.course_id} {course.name}")
print("-" * 40)
print(f"{'Student ID':<14}{'Name':<14}{'Score':<8}")

for enrollment in self.enrollments.values():
if enrollment.course_id != course_id:
continue
student = self.students[enrollment.student_id]
score_text = "N/A" if enrollment.score is None else f"{enrollment.score:.1f}"
print(f"{student.student_id:<14}{student.name:<14}{score_text:<8}")
print()


def print_menu() -> None:
print("1. Add student")
print("2. Add course")
print("3. Enroll")
print("4. Set score")
print("5. Student report")
print("6. Course roster")
print("0. Exit")


def main() -> None:
system = CourseSystem()

while True:
print_menu()
choice = input("Choose: ").strip()

if choice == "1":
student_id = input("Student ID: ").strip()
name = input("Name: ").strip()
system.add_student(student_id, name)

elif choice == "2":
course_id = input("Course ID: ").strip()
name = input("Course name: ").strip()
credit = float(input("Credit: "))
system.add_course(course_id, name, credit)

elif choice == "3":
student_id = input("Student ID: ").strip()
course_id = input("Course ID: ").strip()
system.enroll(student_id, course_id)

elif choice == "4":
student_id = input("Student ID: ").strip()
course_id = input("Course ID: ").strip()
score = float(input("Score: "))
system.set_score(student_id, course_id, score)

elif choice == "5":
student_id = input("Student ID: ").strip()
system.print_student_report(student_id)

elif choice == "6":
course_id = input("Course ID: ").strip()
system.print_course_roster(course_id)

elif choice == "0":
break

else:
print("Invalid choice.")


if __name__ == "__main__":
main()

4.4 测试流程

可以按下面顺序手动测试:

1
2
3
4
5
6
1 -> 202001 -> Ruiqing
2 -> CS101 -> Python -> 3
3 -> 202001 -> CS101
4 -> 202001 -> CS101 -> 92
5 -> 202001
6 -> CS101

5、学习重点

5.1 阶乘和组合

这类题的核心不只是计算结果,而是把阶乘、排列、组合等逻辑拆成清晰函数,并考虑非法输入、边界值和较大结果。它让我开始意识到:同一个数学公式,写成程序时需要额外处理输入、类型和可读性。

5.2 学生课程系统

学生课程系统更像后面后台管理项目的雏形:学生、课程、成绩或选课关系都可以抽象成对象,菜单负责调度操作,数据结构负责保存状态。它训练的不是某个语法点,而是“程序里有哪些角色,它们之间怎么协作”。

6、个人复盘

这门课程的核心价值是第一次从“写出一个答案”转向“组织一个程序”。现在回看,我最该保留的是类的职责划分、菜单程序的状态管理、数据校验和文件持久化思路。之后如果找到原始代码,我会把当时版本和现在更清晰的写法分开记录,方便看到自己的变化。