aoi学院

Aisaka's Blog, School of Aoi, Aisaka University

Python-第19篇《面向对象编程》

导语

前面我们写的代码都是“一股脑”顺序执行,像记流水账。但现实世界的财务系统不是这样的:员工、部门、工资单、报销单…每个都是“独立个体”,互相协作。今天咱们用财务最熟悉的公司组织架构,来理解面向对象编程(OOP):把代码拆成一个个“部门”,每个部门有自己的“职责”和“账本”,既能独立工作又能互相配合。


本篇目标

  • 用公司组织架构理解“类”和“对象”
  • 掌握封装:保护数据不被随意篡改
  • 理解继承:子公司继承总公司制度
  • 体会多态:不同部门对同一指令的不同处理
  • 实战:构建可扩展的员工管理系统

一、不用安装,直接开干

面向对象编程是Python的内置功能,无需额外安装库。准备好我们的老朋友就行:

1
2
3
4
5
# 纯Python,无需安装
import json
from datetime import datetime
from typing import List, Dict
import os

二、类和对象:用公司组织架构理解

2.1 什么是类?什么是对象?

类(Class) = 公司部门章程
定义了“员工应该有什么属性(姓名、工号、薪资),能做什么(上班、请假、报销)”

对象(Object) = 具体的某个员工
根据章程创建的“活生生的人”,比如“张三,工号1001,薪资15000”

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
# ========== 定义"员工"类(部门章程) ==========
class Employee:
"""
员工类 - 就像一个公司的《员工手册》
规定了所有员工应该有的信息和方法
"""

# 类属性:所有员工共享(就像公司的公积金比例)
company_name = "智慧财务科技有限公司"
social_insurance_rate = 0.105 # 五险一金比例

def __init__(self, name: str, employee_id: str, department: str, base_salary: float):
"""
初始化方法:新员工入职时调用
就像新员工填写入职登记表
"""
self.name = name # 实例属性:姓名
self.employee_id = employee_id # 工号
self.department = department # 部门
self.base_salary = base_salary # 基本工资
self.onboard_date = datetime.now().strftime('%Y-%m-%d') # 入职日期
self.is_active = True # 是否在职

print(f"✅ 新员工入职成功:{name}{employee_id})")

def get_monthly_salary(self) -> float:
"""
计算月薪(税后估算)
这是一个"方法",员工可以"做"的计算
"""
insurance = self.base_salary * self.social_insurance_rate
taxable = self.base_salary - insurance - 5000 # 起征点

if taxable <= 0:
tax = 0
elif taxable <= 36000:
tax = taxable * 0.03
else:
tax = taxable * 0.10 - 2520 # 简化计算

return self.base_salary - insurance - tax

def take_leave(self, days: int):
"""请假"""
if days <= 0:
print("❌ 请假天数必须大于0")
return

if days > 10:
print(f"⚠️ {self.name}请假{days}天,需要CEO审批!")
else:
print(f"✅ {self.name}{self.department})请假{days}天已批准")

def __str__(self):
"""打印员工信息"""
return f"【员工】{self.name} | 工号:{self.employee_id} | 部门:{self.department} | 薪资:¥{self.base_salary:,.2f}"

# 测试:创建几个员工对象
print("=" * 50)
print("员工入职模拟")
print("=" * 50)

# 根据"员工手册"(类)创建具体员工(对象)
emp1 = Employee("张三", "E1001", "财务部", 15000)
emp2 = Employee("李四", "E1002", "销售部", 12000)
emp3 = Employee("王五", "E1003", "技术部", 20000)

# 调用对象方法
print(f"\n{emp1}")
print(f"实际月薪:¥{emp1.get_monthly_salary():,.2f}")
emp1.take_leave(3)

print(f"\n{emp2}")
emp2.take_leave(15) # 触发长假期提醒

三、封装:财务室的保险箱

封装 = 保险箱 + 权限管理
内部数据(工资明细)不能随便改,必须通过“规定流程”(方法)访问

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
# ========== 封装演示:薪资保密 ==========
class SalaryManager:
"""
薪资管理器:封装了敏感数据
就像财务部的保险柜,密码只有特定人能开
"""

def __init__(self):
self.__salary_data = {} # 私有属性:双下划线开头,外部无法直接访问
self.__access_log = [] # 访问日志,也是私有的

def add_salary_record(self, emp_id: str, month: str, amount: float):
"""添加薪资记录(只有这个方法能操作数据)"""
key = f"{emp_id}_{month}"
self.__salary_data[key] = {
'金额': amount,
'录入时间': datetime.now().strftime('%Y-%m-%d %H:%M:%S')
}
print(f"✅ 薪资记录已加密保存")

def get_salary(self, emp_id: str, month: str, requester: str) -> float:
"""
获取薪资:需要记录谁在查
就像财务部要求查工资必须登记
"""
key = f"{emp_id}_{month}"

if key in self.__salary_data:
# 记录访问日志
self.__access_log.append({
'查询人': requester,
'员工': emp_id,
'月份': month,
'查询时间': datetime.now().strftime('%Y-%m-%d %H:%M:%S')
})

return self.__salary_data[key]['金额']
else:
print("⚠️ 未找到该员工薪资记录")
return 0

def show_access_log(self):
"""查看访问日志(管理员权限)"""
print("\n" + "=" * 40)
print("薪资查询访问日志")
print("=" * 40)
for log in self.__access_log:
print(f"{log['查询时间']} | {log['查询人']} 查询了 {log['员工']}{log['月份']} 工资")

# 测试封装
salary_mgr = SalaryManager()
salary_mgr.add_salary_record("E1001", "2025-01", 12500)

# 只能通过方法访问
salary = salary_mgr.get_salary("E1001", "2025-01", "人事专员小王")
print(f"查询到薪资:¥{salary:,.2f}")

# 无法直接访问私有属性(会报错)
# print(salary_mgr.__salary_data) # 报错!

# 查看访问日志
salary_mgr.show_access_log()

四、继承:总公司和子公司

继承 = 子公司继承总公司制度 + 自己特色
销售部员工也是员工,但有额外的“提成”属性

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
# ========== 继承演示:不同部门员工 ==========
class SalesEmployee(Employee):
"""
销售部员工:继承自Employee,增加销售提成
就像子公司有自己的特殊规定
"""

def __init__(self, name: str, employee_id: str, base_salary: float, commission_rate: float = 0.05):
# 调用父类的初始化方法(继承总公司的入职流程)
super().__init__(name, employee_id, "销售部", base_salary)
self.commission_rate = commission_rate # 提成比例
self.monthly_sales = 0 # 本月销售额

def record_sales(self, amount: float):
"""记录销售额"""
self.monthly_sales += amount
print(f"✅ {self.name}本月销售额增加¥{amount:,.2f},累计¥{self.monthly_sales:,.2f}")

def get_monthly_salary(self) -> float:
"""
重写父类方法:销售薪资 = 基本工资 + 提成 - 税费
这就是多态:同样的"计算工资"指令,销售和财务执行方式不同
"""
base_salary = super().get_monthly_salary() # 先算基本工资税后
commission = self.monthly_sales * self.commission_rate

# 提成部分也扣税(简化)
commission_tax = commission * 0.10 if commission > 0 else 0

total_salary = base_salary + commission - commission_tax

print(f"💰 {self.name}薪资构成:底薪¥{base_salary:,.2f} + 提成¥{commission:,.2f} - 税费¥{commission_tax:,.2f}")

return total_salary

def __str__(self):
"""重写父类的字符串表示"""
return f"【销售精英】{self.name} | 工号:{self.employee_id} | 提成比例:{self.commission_rate:.1%} | 本月业绩:¥{self.monthly_sales:,.2f}"

class TechEmployee(Employee):
"""
技术部员工:有项目奖金
"""

def __init__(self, name: str, employee_id: str, base_salary: float, project_bonus: float = 0):
super().__init__(name, employee_id, "技术部", base_salary)
self.project_bonus = project_bonus

def get_monthly_salary(self) -> float:
"""技术部薪资:基本工资 + 项目奖金"""
base_salary = super().get_monthly_salary()
total = base_salary + self.project_bonus

print(f"💰 {self.name}薪资构成:底薪¥{base_salary:,.2f} + 项目奖金¥{self.project_bonus:,.2f}")

return total

# 测试继承
print("\n" + "=" * 50)
print("不同部门员工测试")
print("=" * 50)

sales_emp = SalesEmployee("赵六", "E2001", 8000, commission_rate=0.08)
sales_emp.record_sales(50000) # 本月卖5万
sales_emp.record_sales(30000) # 又卖3万

print(f"\n{sales_emp}")
print(f"实际月薪:¥{sales_emp.get_monthly_salary():,.2f}")

tech_emp = TechEmployee("孙七", "E3001", 18000, project_bonus=5000)
print(f"\n{tech_emp}")
print(f"实际月薪:¥{tech_emp.get_monthly_salary():,.2f}")

五、多态:同一条指令,不同反应

多态 = 不同部门对“报销”的不同审批流程
都是调用apply_reimbursement()方法,但财务部和销售部的内部实现完全不同

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
# ========== 多态演示:报销流程 ==========
class Department:
"""部门基类"""

def __init__(self, name: str, manager: str):
self.name = name
self.manager = manager

def apply_reimbursement(self, employee: Employee, amount: float, reason: str):
"""
申请报销:各个部门有不同规则
这就是多态:同样的方法名,不同部门有不同实现
"""
raise NotImplementedError("子类必须实现此方法")

class FinanceDepartment(Department):
"""财务部:报销审核最严"""

def apply_reimbursement(self, employee: Employee, amount: float, reason: str):
print(f"\n【财务部报销审核】")
print(f"申请人: {employee.name}")
print(f"金额: ¥{amount:,.2f}")
print(f"事由: {reason}")

if amount > 5000:
print("⚠️ 金额超过¥5000,需要CFO额外审批")
return False
else:
print("✅ 审核通过,3个工作日内打款")
return True

class SalesDepartment(Department):
"""销售部:为了业绩,报销灵活"""

def apply_reimbursement(self, employee: Employee, amount: float, reason: str):
print(f"\n【销售部报销审核】")
print(f"申请人: {employee.name}(本月业绩¥{employee.monthly_sales if hasattr(employee, 'monthly_sales') else 0:,.2f})")

# 销售业绩好的员工,报销额度自动提高
if hasattr(employee, 'monthly_sales') and employee.monthly_sales > 100000:
print("🎉 业绩优秀,报销快速通道!")
print("✅ 审核通过,1个工作日内打款")
return True

if amount > 10000:
print("⚠️ 金额较大,需要经理审批")
return False
else:
print("✅ 审核通过")
return True

class TechDepartment(Department):
"""技术部:关注是否合理"""

def apply_reimbursement(self, employee: Employee, amount: float, reason: str):
print(f"\n【技术部报销审核】")
print(f"申请人: {employee.name}")
print(f"事由: {reason}")

# 检查是否包含技术关键词
tech_keywords = ['服务器', '域名', '软件', '技术书籍', '培训']
if any(keyword in reason for keyword in tech_keywords):
print("✅ 技术相关支出,优先批准")
return True
else:
print("ℹ️ 非技术支出,按标准流程审批")
return amount <= 3000

# 测试多态
print("\n" + "=" * 50)
print("报销审批测试(多态)")
print("=" * 50)

# 创建部门对象
finance_dept = FinanceDepartment("财务部", "张经理")
sales_dept = SalesDepartment("销售部", "李经理")
tech_dept = TechDepartment("技术部", "王经理")

# 张三(财务部)申请报销
finance_dept.apply_reimbursement(emp1, 8000, "年度审计服务费")

# 赵六(销售部)申请报销
sales_dept.apply_reimbursement(sales_emp, 12000, "客户招待费")

# 孙七(技术部)申请报销
tech_dept.apply_reimbursement(tech_emp, 2500, "购买技术书籍")
tech_dept.apply_reimbursement(tech_emp, 2500, "团队聚餐费")

# 同样的"apply_reimbursement"调用,不同部门有不同实现!

六、实战:构建员工管理系统

整合所有概念,做一个能用的系统。

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
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
# ========== 员工管理系统 - 完整版 ==========
class EmployeeManagementSystem:
"""
员工管理系统:使用面向对象方式管理
"""

def __init__(self, data_file="employees.json"):
self.data_file = data_file
self.employees: List[Employee] = []
self.departments: Dict[str, Department] = {}
self.load_data()
self.init_departments()

def init_departments(self):
"""初始化各部门"""
self.departments["财务部"] = FinanceDepartment("财务部", "张经理")
self.departments["销售部"] = SalesDepartment("销售部", "李经理")
self.departments["技术部"] = TechDepartment("技术部", "王经理")

def add_employee(self):
"""添加员工"""
print("\n" + "=" * 40)
print("添加新员工")
print("=" * 40)

name = input("姓名: ").strip()
emp_id = input("工号: ").strip()
dept = input("部门(财务部/销售部/技术部): ").strip()
salary = float(input("基本工资: ").strip())

if dept == "销售部":
commission = float(input("提成比例(如0.08表示8%): ").strip())
emp = SalesEmployee(name, emp_id, salary, commission)
elif dept == "技术部":
bonus = float(input("项目奖金: ").strip())
emp = TechEmployee(name, emp_id, salary, bonus)
else:
emp = Employee(name, emp_id, dept, salary)

self.employees.append(emp)
print(f"\n✅ 员工添加成功!")
print(emp)

def list_employees(self):
"""列出所有员工"""
print("\n" + "=" * 50)
print("员工列表")
print("=" * 50)

if not self.employees:
print("📭 暂无员工数据")
return

for i, emp in enumerate(self.employees, 1):
print(f"\n{i}. {emp}")

def calculate_all_salaries(self):
"""计算所有员工本月薪资"""
print("\n" + "=" * 50)
print("薪资计算")
print("=" * 50)

if not self.employees:
print("⚠️ 暂无员工数据")
return

total_salary = 0

for emp in self.employees:
salary = emp.get_monthly_salary()
total_salary += salary
print(f" {emp.name}: ¥{salary:,.2f}")

print(f"\n💰 本月工资总支出: ¥{total_salary:,.2f}")

def reimbursement_request(self):
"""报销申请"""
print("\n" + "=" * 40)
print("报销申请")
print("=" * 40)

if not self.employees:
print("⚠️ 暂无员工数据")
return

# 选择员工
self.list_employees()
idx = int(input("选择员工编号(数字): ").strip()) - 1

if idx < 0 or idx >= len(self.employees):
print("❌ 编号错误")
return

emp = self.employees[idx]
amount = float(input("报销金额: ").strip())
reason = input("报销事由: ").strip()

# 获取员工所在部门进行审批
if emp.department in self.departments:
self.departments[emp.department].apply_reimbursement(emp, amount, reason)
else:
print("⚠️ 该部门暂无审批流程")

def save_data(self):
"""保存数据到JSON"""
data = {
'employees': [],
'update_time': datetime.now().strftime('%Y-%m-%d %H:%M:%S')
}

for emp in self.employees:
emp_data = {
'name': emp.name,
'employee_id': emp.employee_id,
'department': emp.department,
'base_salary': emp.base_salary,
'type': type(emp).__name__ # 保存类型,用于后续加载
}

# 保存子类特有属性
if isinstance(emp, SalesEmployee):
emp_data['commission_rate'] = emp.commission_rate
emp_data['monthly_sales'] = emp.monthly_sales
elif isinstance(emp, TechEmployee):
emp_data['project_bonus'] = emp.project_bonus

data['employees'].append(emp_data)

with open(self.data_file, 'w', encoding='utf-8') as f:
json.dump(data, f, ensure_ascii=False, indent=2)

print(f"✅ 数据已保存到 {self.data_file}")

def load_data(self):
"""从JSON加载数据"""
if not os.path.exists(self.data_file):
return

try:
with open(self.data_file, 'r', encoding='utf-8') as f:
data = json.load(f)

self.employees = []

for emp_data in data.get('employees', []):
emp_type = emp_data.get('type', 'Employee')

if emp_type == 'SalesEmployee':
emp = SalesEmployee(
emp_data['name'],
emp_data['employee_id'],
emp_data['base_salary'],
emp_data.get('commission_rate', 0.05)
)
emp.monthly_sales = emp_data.get('monthly_sales', 0)
elif emp_type == 'TechEmployee':
emp = TechEmployee(
emp_data['name'],
emp_data['employee_id'],
emp_data['base_salary'],
emp_data.get('project_bonus', 0)
)
else:
emp = Employee(
emp_data['name'],
emp_data['employee_id'],
emp_data['department'],
emp_data['base_salary']
)

self.employees.append(emp)

print(f"📂 已加载 {len(self.employees)} 名员工数据")

except Exception as e:
print(f"❌ 加载数据失败: {e}")

def main():
"""主菜单"""
ems = EmployeeManagementSystem()

while True:
print("\n" + "=" * 55)
print("员工管理系统")
print("=" * 55)
print("1. 添加员工")
print("2. 查看员工列表")
print("3. 计算全体薪资")
print("4. 报销申请")
print("5. 保存数据")
print("6. 退出")
print("=" * 55)

choice = input("请选择功能: ").strip()

if choice == "1":
ems.add_employee()
elif choice == "2":
ems.list_employees()
input("\n按回车键继续...")
elif choice == "3":
ems.calculate_all_salaries()
input("\n按回车键继续...")
elif choice == "4":
ems.reimbursement_request()
input("\n按回车键继续...")
elif choice == "5":
ems.save_data()
input("\n按回车键继续...")
elif choice == "6":
print("👋 感谢使用,数据已自动保存!")
ems.save_data()
break
else:
print("请输入1-6之间的数字!")

if __name__ == "__main__":
main()

七、知识点加油站

7.1 OOP三大特性速查

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
"""
封装 (Encapsulation):
- __private_attr 私有属性
- public_method() 公共方法
- 数据保护,防止误修改

继承 (Inheritance):
- class Child(Parent): 子类继承父类
- super().__init__() 调用父类方法
- 代码复用,逻辑分层

多态 (Polymorphism):
- 同名方法,不同实现
- 子类重写父类方法
- 接口统一,行为多样
"""

7.2 isinstance()和type()的区别

1
2
3
4
5
emp = SalesEmployee("张三", "E1001", 8000)

type(emp) == SalesEmployee # True,精确匹配
isinstance(emp, SalesEmployee) # True,是销售员工
isinstance(emp, Employee) # True,也是员工(继承关系)

八、总结

  • ✅ 用公司架构理解类和对象
  • ✅ 封装保护敏感薪资数据
  • ✅ 继承实现不同部门员工
  • ✅ 多态实现差异化报销流程
  • ✅ 完整的员工管理系统

下篇预告

第20篇《异常处理:让程序更健壮》——代码出错不可怕,学会优雅处理,让程序“百折不挠”!


🤖 Powered by Kimi K2 Thinking 💻 内容经葵葵🌻审核与修改