学生管理系统

浏览 1913

课文

需求概述

新的一个学期开始了,开学的第一天辅导员找到你,说班里学生太多,想让你帮着做一个学生管理系统。

你一想这不是很简单, 啪啪啪敲下了几行代码便交了上去。

# 用数据与字典结合的形式保存数据
data = []
while True:
    inp = input('请输入新增、查看或退出:\n')
    if inp == '新增':
        # 接收用户数据,并去除前后空格
        inp = input('输入姓名、性别、年龄,用空格作区分:\n').strip()
        name, gender, age = inp.split(' ')

        # 将数据拆分出来并以字典形式添加到数组中
        data.append(
            dict(
                name=name,
                gender=gender,
                age=age,
            )
        )
    elif inp == '查看':
        print('当前共有%s名学生' % len(data))
        # 循环打印数据
        for item in data:
            print(
                '姓名:{name},性别:{gender},年龄:{age}'.format(
                    name=item['name'], gender=item['gender'], age=item['age']
                )
            )
    elif inp == '退出':
        break
    else:
        print('输入错误请重新输入')

    print('\n')

你运用了之前所学到的知识,使用了列表与字典类型保存数据。 使用了 while 循环接收用户的操作, 用 input 接收输入并对其字符串拆分。

pickle 序列化

美美地等着辅导员夸奖的你,等来了辅导员的电话,他告诉你他花半小时录入的数据在重启程序后消失, 你一拍腿大呼忘记将其持久化到硬盘,于是便运用了上个章节学到的文件操作进行修改。

因为文件的操作写入时接收二进制或字符串类型,便需要将我们数组与字典组合的类型序列化成字符串加以保存。

什么是序列化?

序列化便是以一定规范将对象转成二进制或字符,等到需要时再将其解析回对象。 常在需要保存对象到文件或数据库,再或者网络传输时使用。

在这里我们使用内置的pickle模块来序列化和反序列化。

import os
import pickle

# 用大写变量名来表示一些常量
DATA_PATH = './data'

# 用数据与字典结合的形式保存数据
data = []

# 判断文件是否存在
if os.path.exists(DATA_PATH):
    with open(DATA_PATH, 'r') as f:
        data_str = f.read()
        data = pickle.loads(data_str)

while True:
    inp = input('请输入新增、查看或退出:\n')
    if inp == '新增':
        # 接收用户数据,并去除前后空格
        inp = input('输入姓名、性别、年龄,用空格作区分:\n').strip()
        name, gender, age = inp.split(' ')

        # 将数据拆分出来并以字典形式添加到数组中
        data.append(
            dict(
                name=name,
                gender=gender,
                age=age,
            )
        )
    elif inp == '查看':
        print('当前共有%s名学生' % len(data))
        # 循环打印数据
        for item in data:
            print(
                '姓名:{name},性别:{gender},年龄:{age}'.format(
                    name=item['name'], gender=item['gender'], age=item['age']
                )
            )
    elif inp == '退出':
        break
    else:
        print('输入错误请重新输入')

    print('\n')

with open(DATA_PATH, 'w') as f:
    f.write(pickle.dumps(data))

我们运用之前学到的知识,在一开始读取文件并用 loads 将二进制解析成数组。 并在之后退出程序时将 data 数组 dumps 成二进制。

在你刚写完的时候,辅导员来了电话,说他还需要更新数据功能。

你一寻思,辅导员是典型的甲方心态。一开始自己心里没个数,一会说加个功能,一会说修改个功能。

代码经常修改的话, 会影响到开发封闭原则,各模块相互引用如盘丝洞般错综复杂,不小心就变成了屎山。

所以你想到了可以面向对象的方式重构代码,面向对象的三大特征是抽象、继承、多态,可以让你很容易地应对之后的修改与功能的新增。

面向对象设计是对付不合理甲方的神器。

首先我们确定三个类。

class Student:
    # 学生对象
    def __init__(self, name, gender, age):
        pass

    def __str__(self):
        # 打印时的显示
        pass

class Op:
    # 所有操作类的父类

    @staticmethod
    def hander(data):
        raise NotImplementedError()

class Manager:
    # 负责程序的管理,数据的读取与保存

    def register(self, op):
        # 注册操作
        pass

    def unregister(self, op):
        # 取消注册
        pass

    def load(self):
        # 从文件读取数据
        pass


    def save(self):
        # 保存数据到文件
        pass

    def loop(self):
        # 执行循环
        pass

以上就是我们大致定义到的接口,针对其分别进行实现。

import os
import pickle

DATA_PATH = './data'


class Student:
    def __init__(self, name, gender, age):
        self.name = name
        self.gender = gender
        self.age = age

    def __str__(self):
        return '姓名:{name},性别:{gender},年龄:{age}'.format(
            name=self.name, gender=self.gender, age=self.age
        )

class Op:
    action = ''

    @staticmethod
    def handle(data):
        raise NotImplementedError()


class Manager:
    ops = {}
    data = []

    def __init__(self):
        self.load()

    def load(self):
        # 从文件读取数据
        # 判断文件是否存在
        if os.path.exists(DATA_PATH):
            with open(DATA_PATH, 'rb') as f:
                self.data = pickle.load(f)

    def save(self):
        # 保存数据到文件
        with open(DATA_PATH, 'wb') as f:
            pickle.dump(self.data, f)


    def register(self, op):
        # 注册操作
        self.ops[op.action] = op

    def unregister(self, op):
        # 取消注册
        if op.action in self.ops:
            self.ops[op.action]

    def loop(self):
        # 执行循环
        while True:
            inp = input(
                '请输入%s或退出:\n' % ','.join([action for action in self.ops.keys()])
            )

            if inp == '退出':
                break
            if inp in self.ops:
                self.ops[inp].handle(self.data)
            else:
                print('输出错误')

        self.save()

我们完成了以上的东西, 这时我们定义一个 OpAdd 类 与 OpList 继承自 Op 类。 并将其注册到 manager 中,运行我们的程序。

class OpAdd(Op):
    action = '增加'

    @staticmethod
    def handle(data):
        inp = input('输入姓名、性别、年龄,用空格作区分:\n')
        name, gender, age = inp.split(' ')
        data.append(Student(name, gender, age))
        print('增加成功')
        return data


class OpList(Op):
    action = '查看'

    @staticmethod
    def handle(data):
        print('当前共有%s名学生' % len(data))
        # 循环打印数据
        for student in data:
            print(student)


manager = Manager()
manager.register(OpAdd)
manager.register(OpList)

# 执行
manager.loop()

到此完成了我们的程序,测试一下!

请输入增加,查看或退出:
增加
输入姓名、性别、年龄,用空格作区分:
小明 男 20
增加成功
请输入增加,查看或退出:
增加
输入姓名、性别、年龄,用空格作区分:
鸣人 男 18
增加成功
请输入增加,查看或退出:
查看
当前共有2名学生
姓名:小明,性别:男,年龄:20
姓名:鸣人,性别:男,年龄:18
请输入增加,查看或退出:      
退出

实战

1.甲方的需求是无止尽的,完善上面的程序!

  • 增加删除功能
  • 增加按名字搜索功能

解析

1.甲方的需求是无止尽的,完善上面的程序!

  • 增加删除功能
  • 增加按名字搜索功能
class OpDel(Op):
    action = '删除'

    @staticmethod
    def handle(data):
        inp = input('输入要删除的姓名:\n')
        for student in data[:]:
            if student.name == inp:
                data.remove(student)
                print('删除了:%s' % student)


class OpSearch(Op):
    action = '搜索'

    @staticmethod
    def handle(data):
        inp = input('输入要搜索的姓名:\n')
        for student in data:
            if inp in student.name:
                print('搜索到:%s' % student)
点击查看

评论

登录参与讨论

刘德华山东分华

该内容已删除

2021-04-16

回复

style

测试回复

@刘德华山东分华

2021-05-19

回复

非洲小白脸

2021-08-20

回复

非洲小白脸

是w改成wb 手误

@非洲小白脸

2021-08-20

回复

非洲小白脸

r改成rb

@非洲小白脸

2021-08-20

回复

非洲小白脸

讲错了是w改成wb  不是rb哈 手误

2021-08-20

回复

非洲小白脸

r改成rb

2021-08-20

回复

非洲小白脸

解析1中删除代码存在问题,出现同名现象时只会删除第一个出现的 原因是,删除list中的元素后,list的实际长度变小了,但是循环次数没有减少,依然按照原来list的长度进行遍历,所以会造成索引溢出。 解决办法1.拷贝原列表 data[:]是对原列表的一个拷贝,然后对原列表进行删除操作,这种做法缺点是太占空间 2.从后往前遍历列表,删除 3.使用set去重 并使用sort保持原排序 这种做法感觉不适用于实际,当然实际也不应该用名字删除而是用学号删除,这里不贴出代码了

2021-08-20

回复

西瓜lalavilaxxww

观察很细致,列表删除确实存在问题,我完善一下代码。

@非洲小白脸

2021-09-04

回复

共 5 条
  • 1
前往
  • 1