Python面向对象编程的经典案例

项目名称

学校人事管理系统

一、类设计

我们大致上将学校中的人分为三个类别Person,Student和Staff。
Person是基本人员,Student是学生,Staff是教职员工。
他们都具有姓名,性别,出生年月等属性,而学生有成绩、学号等特殊属性,教职员工有工资、入职时间等特殊属性,所以可以将Person作为一个基类,Student和Staff去继承这个基类。
另外,为了防止存入不符要求的信息,我们可以自定义异常类来处理这个问题。

二、实现步骤

1.项目目录
先创建一个python package,叫做学校人事管理系统的类设计。然后在此package下新建py文件,叫做异常类.py。
后面再创建公共人员类.py,学生类.py和教职工类.py。
Python面向对象编程的经典案例_第1张图片

2.异常类

自定义异常类

class PersonTypeError(TypeError):
    pass

class PersonValueError(ValueError):
    pass

用来处理输入信息的类型不对或数值异常。

3.公共人员类

import datetime
from Abnormal import PersonTypeError, PersonValueError


class Person:
    _num = 0

    def __init__(self, name, sex, birthday, ident):
        """
        实例对象的基础信息
        :param name: 姓名
        :param sex: 性别
        :param birthday: 出生年月
        :param ident: 编号
        """
        if not (isinstance(name, str) and sex in ('男', '女')):
            # 检查姓名是否是字符串,性别是男还是女
            raise PersonValueError(name, sex)
        try:
            birth = datetime.date(*birthday)
        except:
            raise PersonValueError('wrong date:', birthday)
        self._name = name
        self._sex = sex
        self._birthday = birth
        self._id = ident
        # 实例计数
        Person._num += 1

    def id(self):
        return self._id

    def name(self):
        return self._name

    def sex(self):
        return self._sex

    def birthday(self):
        return self._birthday

    def age(self):
        # 返回年龄
        return datetime.date.today().year - self._birthday.year

    def set_name(self, name):
        """
        修改名字
        :param name: 新名字
        :return: void
        """
        if not isinstance(name, str):
            raise PersonValueError('name:', name)
        self._name = name

    def __lt__(self, other):
        """
        根据编号对人物对象排序
        :param other: 对象
        :return: 对象的id
        """
        if not isinstance(other, Person):
            raise PersonTypeError(Person)
        return self._id < other._id

    @classmethod
    def num(cls):
        # 类方法,用来获得类中人员的计数值
        return Person._num

    def __str__(self):
        # 提供对象的基本信息
        return ' '.join((self._id, self._name, self._sex, str(self._birthday)))

    def details(self):
        # 提供完整信息
        return ', '.join(('编号: ' + self._id,
                          '姓名: ' + self._name,
                          '年龄: ' + str(self.age()),
                          '性别: ' + self._sex,
                          '出生年月: ' + str(self._birthday)))

if __name__ == '__main__':
    p1 = Person('小红', '女', (2000, 1, 30), '20181122')
p2 = Person('小明', '男', (2000, 4, 21), '20181130')
p3 = Person('小华', '男', (1999, 12, 23), '20181105')

person_list = [p1, p2, p3]
for person in person_list:
    print(person)
    print(person.details())

person_list.sort()
print('\n\n--------------------根据编号排序后:')
for person in person_list:
    print(person)
    print(person.details())

print(f'公共人员类中共有{Person.num()}个人员信息')

代码里对人员的基本信息:姓名,性别,出生年月,年龄等信息做了初始化并记录。下面我们运行一下该程序。
Python面向对象编程的经典案例_第2张图片

4.学生类
学生是学校里面最主要的人员组成,除了有公共人员的基本属性外,还有选课,成绩,学号等属性。因此,我们需要让Student类在继承Person类的同时,为它新加入一些独有的属性。

import datetime
from Person import Person
from Abnormal import PersonTypeError, PersonValueError


class Student(Person):
    _id_num = 0

    def __init__(self, name, sex, birthday, department):
        """
        学生对象的基础信息
        :param name: 学生姓名
        :param sex: 学生性别
        :param birthday: 学生出生年月
        :param department: 学生院系
        """
        Person.__init__(self, name, sex, birthday, Student._id_gen())
        self._department = department
        self._enroll_date = datetime.date.today()  # 入学报道日期
        self._courses = {}

    @classmethod
    def _id_gen(cls):
        """
        生成学号
        :return: 学生学号
        """
        cls._id_num += 1
        year = datetime.date.today().year
        # 学生学号的首位为1,以区分教职工号,入学年份以4位十进制编入学号,序号以5位十进制编入学号
        return '1{:04}{:05}'.format(year, cls._id_num)

    def department(self):
        return self._department

    def set_course(self, course_name):
        """
        选课
        :param course_name: 课程名
        :return: void
        """
        self._courses[course_name] = None

    def set_score(self, course_name, score):
        """
        为选定的课打分数
        :param course_name: 课程名
        :param score: 分数
        :return: void
        """
        if course_name not in self._courses:
            raise PersonValueError('No course was found:', course_name)
        self._courses[course_name] = score

    def scores(self):
        # 得到所有科目的成绩列表
        return [(course_name, self._courses[course_name]) for course_name in self._courses]

    def details(self):
        # 提供完整信息
        return ', '.join((Person.details(self),
                          '入学日期: ' + str(self._enroll_date),
                          '院系: ' + self._department,
                          '选课情况: ' + str(self.scores())))

    def __lt__(self, other):
        """
        根据编号对学生对象倒序排序
        :param other: 对象
        :return: 对象的id
        """
        if not isinstance(other, Student):
            raise PersonTypeError(Student)
        return self._id > other._id


if __name__ == '__main__':
    st1 = Student('小红', '女', (2000, 1, 30), '外语')
    st2 = Student('小明', '男', (2000, 4, 21), '物理')
    st3 = Student('小华', '男', (1999, 12, 23), '计算机')
    st4 = Student('小丽', '女', (2002, 8, 6), '法学')

    st1.set_course('英语')
    st1.set_score('英语', 85)

    st2.set_course('体育')
    st2.set_score('体育', 88)

    st3.set_course('python')
    st3.set_score('python', 95)

    st4.set_course('刑法')
    st4.set_score('刑法', 93)

    student_list = [st1, st2, st3, st4]
    for student in student_list:
        print(student)
        print(student.details())

    student_list.sort()
    print('\n\n--------------------根据学号排序后:')
    for student in student_list:
        print(student)
        print(student.details())

    print(f'学生类中共有{Student.num()}个人员信息')

我们来运行一下程序。可见学生在Person有属性的基础上,又多了几个属性。
Python面向对象编程的经典案例_第3张图片

5.教职工类
同理,教职工类在继承Person类时,需要加入入职时间,薪资等属性。

import datetime
from Person import Person
from Abnormal import PersonTypeError, PersonValueError


class Staff(Person):
    _id_num = 0

    def __init__(self, name, sex, birthday, entry_date=None):
        """
        教职工对象的基础信息
        :param name: 教职工姓名
        :param sex: 教职工性别
        :param birthday: 教职工出生年月
        :param entry_date: 教职工入职日期
        """
        super().__init__(name, sex, birthday, Staff._id_gen(birthday))
        if entry_date:
            # 传入入职日期则记录为传入的日期值
            try:
                self._entry_date = datetime.date(*entry_date)
            except:
                raise PersonValueError('wrong date:', entry_date)
        else:
            # 未传入入职日期,记录为当天入职
            self._entry_date = datetime.date.today()
        self._salary = 5000  # 教职工月薪
        self._department = '未知'  # 教职工所在科系
        self._position = '未知'  # 教职工职位

    @classmethod
    def _id_gen(cls, birthday):
        """
        生成教职工号
        :return: 教职工号
        """
        cls._id_num += 1
        birth_year = datetime.date(*birthday).year
        # 教职工号的首位为0,以区分学生工号,出生年份以4位十进制编入教职工号,序号以5位十进制编入教职工号
        return '0{:04}{:05}'.format(birth_year, cls._id_num)

    def entry_date(self):
        return self._entry_date

    def set_salary(self, salary):
        """
        设置教职工薪水
        :param salary: 月薪
        :return: void
        """
        if not type(salary) is int:
            raise TypeError
        self._salary = salary

    def set_position(self, position):
        """
        设置教职工职位
        :param position: 职位
        :return: void
        """
        self._position = position

    def set_department(self, department):
        """
        设置教职工科系
        :param department: 科系名
        :return: void
        """
        self._department = department

    def details(self):
        # 提供完整信息
        return ', '.join((super().details(),
                          '入职日期: ' + str(self._entry_date),
                          '院系: ' + self._department,
                          '职位: ' + self._position,
                          '工资: ' + str(self._salary)))

    def __lt__(self, other):
        """
        根据编号对教职工对象倒序排序
        :param other: 对象
        :return: 对象的id
        """
        if not isinstance(other, Staff):
            raise PersonTypeError(Staff)
        return self._id > other._id


if __name__ == '__main__':
    st1 = Staff('赵刚', '男', (1980, 1, 30), (2013, 10, 1))
    st2 = Staff('张强', '男', (1988, 4, 21), (2010, 12, 2))
    st3 = Staff('王小红', '女', (1990, 12, 23))

    st1.set_salary(8000)
    st1.set_department('物理')
    st1.set_position('教授')

    st2.set_salary(6000)
    st2.set_department('计算机')

    st3.set_salary(10000)
    st3.set_position('副教授')

    staff_list = [st1, st2, st3]
    for staff in staff_list:
        print(staff)
        print(staff.details())

    staff_list.sort()
    print('\n\n--------------------根据教职工号排序后:')
    for staff in staff_list:
        print(staff)
        print(staff.details())

    print(f'教职工类中共有{Staff.num()}个人员信息')

运行这个程序。
Python面向对象编程的经典案例_第4张图片

总结

这个例子不算很难,但涉及到很多有关Python的类的基础知识,比如Python自定义异常类,类继承,方法重写,类方法的使用,部分魔术方法的使用等,让大家能快速了解一些Python的面向对象用法。
在这个例子的基础上作以修改,再结合Mysql数据库和基于tkiinter或者PyQt的GUI编程,就能打造出一个比较简单的学校人员信息的记录和管理系统了。

你可能感兴趣的:(人生苦短-我用Python,python)