昨非 发表于 2020-9-19 16:32:28

【详解】python信息管理系统【新】

本帖最后由 昨非 于 2020-10-14 18:33 编辑

【详解】python信息管理系统

一、前言
    花了整整一天时间搞了个python的信息管理系统,完成一大半了突然发现,鱼c已经有@zltzlt大佬做过了,而且功能十分齐全(比我做的好),一时间有点儿懊恼,大家能理解这种心理就好...
    毕竟鱼c只有三种帖子最火,第一种——官方教程等,这个理所当然,没啥意见;第二种——pygame小游戏诸如此类,毕竟原版教程就是冲着这个去的,忍了;第三种——爬取妹子图,花样爬取妹子图,爬取花样妹子图......,我:????
    转念一想,作为后来者,目前我们所做的东西绝大多数都是前人做过的,这本就是一种“前人栽树,后人乘凉”的体现。代码上,我承认以我入门不久的水平是不可能做到超越,不过我可以把整个程序描述的更详细点儿。
我承认做不到100%的原创,也感谢前人给的参考,具体内容如下,帖子的顺序基本上算是还原了整个程序的制作过程了。
注:
本篇只是简单完成了信息管理系统的基本增删改查功能并注以解释,其余功能未完成或尚待优化之处,今后笔者会尽力更新的,还望读者见谅。
初次运行,请将【数据存储】部分students列表定义代码解除注释并将下方两行读取文件注释掉,避免因json文件不存在而报错。这一不足笔者后续会改善的,见谅
(完成不易,求大家评分支持!!)


二、代码及详解

1、信息管理系统框架

首先要完成的是打印出一个交互界面(相信大家学c过语言的都有经验),本质上就是一个菜单+死循环,在死循环中,通过input()函数满足交互需要,并依照菜单所示,完成分支的框架搭建,这部分没什么难度,附上代码和效果图:

#打印菜单,提示如何操作:
def menu():
    #菜单以多行字符串打印
    str_menu = """
\t欢迎使用【学生信息管理系统】
********************************************
请输入您想要进行的操作:
1.添加指定信息
2.显示全部信息
3.查询指定信息
4.删除指定信息
5.修改指定信息
6.打印操作提示

0.退出系统
********************************************
"""
    print(str_menu)

menu()

#主体循环
while True:
    #输入操作:
    action = input("请输入您想要进行的操作:")
   
    #分支判断
    #1.添加指定信息
    if action == "1":
      print("【添加指定信息】")
      pass
   
   
    #2.显示全部信息
    elif action == "2":
       print("【显示全部信息】")
       pass
   
    #3.查询指定信息
    elif action == "3":
       print("【查询指定信息】")
       pass
   
    #4.删除指定信息
    elif action == "4":
       print("【删除指定信息】")
       pass
   
    #5.修改指定信息
    elif action == "5":
       print("【修改指定信息】")
       pass
   
    #6.打印操作提示
    elif action == "6":
       print("【打印操作提示】")
       pass
   
    #0.退出系统
    elif action == "0":
      print("退出【信息管理系统】,欢迎下次使用!")
      break
    #输入错误:
    else:
      print("输入操作不存在,请按提示正确输入!")

https://s1.ax1x.com/2020/09/19/w5xXVA.png


2、选择数据存储方式

我们知道,数据可以保存在内存中,也可以保存在本地。
在内存中保存的数据,只在程序执行过程中存在,具有一些局限性,因此我们更提倡另一种方式——以文件形式保存在本地。
可选的文件格式有很多种,诸如:txt,csv, xml,json,html 以及小甲鱼讲过的pkl, 这里我选择数据格式为json,想用pkl的可以参考https://fishc.com.cn/thread-144923-1-1.html
针对信息管理系统:多个学员,以列表存储;每个学员,以字典保存各属性及数据;故:列表包含字典来存储。
样例数据为:
students = [
      {'name':'李四','chinese':'64','math':'65','English':'66','total':195},
      {'name':'王五','chinese':'65','math':'65','English':'66','total':196},
      {'name':'赵六','chinese':'66','math':'65','English':'66','total':197}
      ]


3、功能实现-打印

   有了数据,我们从简单的开始,先实现【全部数据打印】:
   即:遍历列表打印字典的值:

https://s1.ax1x.com/2020/09/19/wI98aj.png


以*解包,(加入分隔符)效果如下:

https://s1.ax1x.com/2020/09/19/wI9rdJ.png

这部分的代码如下:

#显示全部信息
def print_info():
    print('姓名    语文    数学    英语    总分')
    for student in students:
      print(*student.values(),sep="   ")



4、功能实现-插入

我们知道,每个学员的信息以字典的形式储存在列表students中,所以插入分为两步;
【收集信息】->【添加到students列表】
收集信息时应注意,input函数返回的为字符串类型,所以作为成绩相加应该转为int
代码及效果图如下:

#添加指定信息
def insert():
    #收集(输入)信息
    name = input('请输入学生姓名:')
    chinese = int(input('请输入学生语文成绩:'))
    math = int(input('请输入学生数学成绩:'))
    English = int(input('请输入学生英语成绩:'))
    total = chinese + math + English
   
    #添加到students列表中
    students.append(
             {'name':name,'chinese':chinese,'math':math,'English':English,'total':total}
            )


https://s1.ax1x.com/2020/09/19/wICB1P.png


5、功能实现-查找

查找的本质是从students列表里面查找,但实际的操作对象确实内部的字典。
因此,需要将列表数据一个个取出来(遍历),然后对比字典name项。
存在一个问题,在找不到的情况下能直接搭配if使用else吗?

错误代码:


def search():
    name = input('请输入需查询学生姓名:')
   
    for student in students: #遍历列表
      if name == student['name']: #对比字典的name
            print('姓名    语文    数学    英语    总分')
            print(*student.values(),sep="   ")
         
      else:
            print("该学生不存在!")

产生的效果如下:


https://s1.ax1x.com/2020/09/19/wIPNuT.png

【解决方案】:else与for搭配使用,在执行完for后会执行else
众鱼友:好像还是不对啊?
当然,需要在if判断查找到后加一个break,跳出for循环,之后的else自然就不会再执行了。反之,当没有查找到时,自然就不会执行break,else照常打印。

正确代码:

def search():
    name = input('请输入需查询学生姓名:')
   
    for student in students: #遍历列表
      if name == student['name']: #对比字典的name
            print('姓名    语文    数学    英语    总分')
            print(*student.values(),sep="   ")
            break
    else:
      print("该学生不存在!")

效果图:

https://s1.ax1x.com/2020/09/19/wIPygx.png



5、功能实现-删除和修改

自不必多说,苍老师说过:“能找到,就能为所欲为”
你会发现这部分和查找何其相似(搬砖暴露了)。
代码如下:

#删除指定信息√
def delete_info():
    name = input('请输入需删除的学生姓名:')
   
    for student in students: #遍历列表
      if name == student['name']: #对比字典的name
            students.remove(student)
            break
    else:
      print("该学生不存在!")

#修改指定信息√
def modify_info():
    name = input('请输入需修改的学生姓名:')
   
    for student in students: #遍历列表
      if name == student['name']: #对比字典的name
            student['name']= input('请输入更新后学生姓名:')
            student['chinese'] = int(input('请输入更新后学生语文成绩:'))
            student['math']= int(input('请输入更新后学生数学成绩:'))
            student['English'] = int(input('请输入更新后学生英语成绩:'))
            student['total']= student['chinese'] +student['math'] + student['English']
            break
    else:
      print("该学生不存在!")

效果图如下:


https://s1.ax1x.com/2020/09/19/wIFZm8.png

https://s1.ax1x.com/2020/09/19/wIFKYj.png


6、功能实现-保存与后期读取

本程序默认退出程序时自动将内存中的students列表以json格式保存到本地。代码位于最后部分以with语句嵌入:

#0.退出系统(自动保存到本地json形式)
    elif action == "0":
      print("退出【学生信息管理系统】")
      print("欢迎下次使用!")
      with open("data.json",mode='w',encoding="utf-8") as f:
             #将对象写入文件而不改变格式
            #f.write(json.dumps(students)) #这样写json格式中文会是“乱码”
            f.write(json.dumps(students,ensure_ascii=False))
      break



所谓“乱码”即为json格式里中文的sacii码,在dumps方法中加入ensure_ascii=False参数即可正确显示中文内容。
json格式中文“乱码”及修正后效果图:

https://s1.ax1x.com/2020/09/19/wIAwsx.png          https://s1.ax1x.com/2020/09/19/wIADeK.png



后期读取代码在【数据存储】部分,这部分可与初始化时students列表相互替代:
with open("data.json",mode='r',encoding="utf-8") as f:
    students = json.loads(f.read()) #注:read读取出来的时字符串,需要转为json对象




三、全部代码

**** Hidden Message *****


四、更新部分

1、更新内容概述:

(1)、数据存储部分,排除初次运行时json文件不存在需要手动修改代码的不便
(2)、部分注释内容不准确
(3)、数据结构中加入唯一ID解决按名字查找的重名问题
(4)、添加(多标准)排序功能



2、具体内容


关于问题一,笔者通过加入打开文件的异常处理解决,这样即保留了必要的初始数据,也不会影响到后续读取文件已有内容。
代码如下:

try:
    with open("data.json",mode='r',encoding="utf-8") as f:
      students = json.loads(f.read())
      #注:read读取出来的时字符串,需要转为json对象

except FileNotFoundError:
    students = [#初始数据,初次运行时避免文件打开报错
      {'id':'20200001','name':'李四','chinese':'64','math':'65','English':'66','total':195},
      {'id':'20200002','name':'王五','chinese':'65','math':'65','English':'66','total':196},
      {'id':'20200003','name':'赵六','chinese':'66','math':'65','English':'66','total':197}
      ]



关于问题二、三,内容在此不作赘述,详情请参考下方更新后的代码,有不足或可改进之处,欢迎评论指出。


新增排序功能部分:

列表的sort方法可以实现将一个列表中元素按照升序排列。然后,sort方法还可以按照我们选定的标准进行元素排序。

list.sort(key=None, reverse=False)

key:匹配一个一元的函数或者表达式,按照这个值决定对应元素在列表中的位置。这里我们定义一个函数sort_key()来作为标准,并利用交互界面提供给用户选择标准的操作空间。
reverse: 一个布尔值,若是True,表示要逆序即从大到小,False表示顺序从小到大。
排序部分代码如下:


#自定义排序(按各成绩)
def sorted_by_grade():
    sort_way = input(
                "请选择排序方式:\n"
                "1按语文成绩排序\n"
                "2按数学成绩排序\n"
                "3按英语成绩排序\n"
                "0按总成绩排序")
    while sort_way not in ['0', '1', '2', '3']:
      sort_way = input(
                  "输入无效!请选择排序方式:\n"
                  "1按语文成绩排序\n"
                  "2按数学成绩排序\n"
                  "3按英语成绩排序\n"
                  "0按总成绩排序")
    if sort_way == '0':
      def sort_key(x):
            return x['total']
    elif sort_way == '1':
      def sort_key(x):
            return int(x['chinese'])
    elif sort_way == '2':
      def sort_key(x):
            return int(x['math'])
    else:
      def sort_key(x):
            return int(x['English'])
    new_students = sorted(students, key=sort_key, reverse=True)
    print(title) #打印
    for student in new_students:
      print(*student.values(),sep="\t")


排完序后,可以由用户选择是否保存到本地,代码如下:

    #保存入文件
    save_in = input("是否将默认学号顺序存入文件 (y/n)?")
    while save_in not in ['y', 'n']:
      save_in = input("输入无效!是否保存 (y/n)?")
    if save_in == 'y':
         f = open("data.json",mode='w',encoding="utf-8")      
         f.write(json.dumps(new_students,ensure_ascii=False))
         print('保存成功!')
    else:
      print('排序结果如上!')
      print('如之后还有需要,请重新排序或选择存入文件!')


但笔者在测试时却出现了问题,即:排序后结果正确打印,选择保存到本地后,在此调用print_info()函数发现,打印顺序仍为排序前的顺序。整体语法上没有报错,那么问题只会出现在逻辑上。

经反复检查,发现问题所在:
函数中排序后的new_students列表是区别于students列表的局部变量,而在打印函数中,打印的始终是students列表。
有鱼友可能会问,退出系统(自动保存)后再运行不就好了吗?
但是还是不对的:退出系统的自动保存students 会覆盖排序后的new_students的保存效果。

为此,笔者专门去查看了一下@zltzlt的代码(连接见上文),发现他的每个函数都是独立打开文件并关闭实现操作的,而笔者的初衷是将students列表作为自始至终的操作对象。

【解决办法】:在调用环节嵌入文件读取,对new_students列表刚刚保存进去的数据进行读取,并依旧使用students作为列表名称。

代码如下:


    #7.恢复默认顺序(学号顺序)
    elif action == "7":
      print("【恢复默认学号顺序】")
      sorted_by_id()#存入重新打开
      with open("data.json",mode='r',encoding="utf-8") as f:
            students = json.loads(f.read())
      
    #8.自定义排序(按各成绩)
    elif action == "8":
      print("【自定义排序(按各成绩)】")   
      sorted_by_grade()#存入重新打开
      with open("data.json",mode='r',encoding="utf-8") as f:
            students = json.loads(f.read())
      


完成排序的效果图如下:
id默认排序:
https://s1.ax1x.com/2020/09/23/wjMEXn.png

成绩排序:
https://s1.ax1x.com/2020/09/23/wjG8je.png


3、更新后全部代码

**** Hidden Message *****

五、结语

   感谢众多鱼友提出的宝贵建议,根据这几天大家的提示,我把我能做出来的部分都做了,可能依旧微不足道。
至今还没有完成的内容:
一、加入图形化界面:这个笔者学的不到家,还做不出来,不过今后如果我学会了,可以考虑补上。
二、调整数据结构以优化运行速度:首先感谢您的宝贵意见,但是以笔者的水平,只能说暂时还考虑不到哪一步了,所以十分抱歉。

   这篇帖子从代码到排版花了好久才完成,首先是真正意义上体会了一遍小甲鱼作扩展阅读之类教程的不容易。和甲鱼哥的目的差不多,这篇内容主要面向新手,所以没有什么太过硬核的东西,之前有鱼友评论说有些内容描述不准确,希望理解下,谢谢。
   内容上,我已经尽力完善了整个信息管理系统的功能,我知道这类代码随便一搜就是一大堆,这也是我在篇头说看到已经有人发过了而感到有些负面情绪的原因。但我也可以肯定,我应该是这里面写的最详细的一批了。

比不过大佬的游戏、爬虫那么吸引眼球,但也求一波评分支持!
Best wishes to FishC !

昨非 发表于 2020-9-23 11:46:47

已更新至2.0版本,跪求评分支持!

昨非 发表于 2020-9-19 16:35:37

@不二如是 @zltzlt 这个能申精吗{:10_266:}(新人正儿八经发的第一帖,求通过)

hornwong 发表于 2020-9-19 16:46:27

感谢分享!

昨非 发表于 2020-9-19 17:04:55

hornwong 发表于 2020-9-19 16:46
感谢分享!

没事,欢迎评论领鱼币{:10_256:}

小甲鱼的铁粉 发表于 2020-9-19 17:26:50

{:10_275:}

昨非 发表于 2020-9-19 17:33:43

小甲鱼的铁粉 发表于 2020-9-19 17:26


{:10_266:}没人理我伤心{:10_266:}

巴巴鲁 发表于 2020-9-19 18:13:56

支持支持
前几天入python,希望有一天我也能在python写出数据库
到时希望各位大佬多多回答我的提问{:10_256:}

昨非 发表于 2020-9-19 18:40:14

巴巴鲁 发表于 2020-9-19 18:13
支持支持
前几天入python,希望有一天我也能在python写出数据库
到时希望各位大佬多多回答我的提问{:10_2 ...

我水平有限,一起学吧{:10_266:}

开心小傻猪 发表于 2020-9-19 18:46:45

支持

lixiangyv 发表于 2020-9-19 18:47:10

感谢分享~~

鱼的七秒记忆 发表于 2020-9-19 21:20:30

{:10_256:}

不二如是 发表于 2020-9-20 08:29:20

昨非 发表于 2020-9-19 16:35
@不二如是 @zltzlt 这个能申精吗(新人正儿八经发的第一帖,求通过)

嗯嗯,可以哦!!

昨非 发表于 2020-9-20 08:40:28

不二如是 发表于 2020-9-20 08:29
嗯嗯,可以哦!!

谢谢肯定{:10_266:}

wzdr 发表于 2020-9-20 08:53:22

{:10_256:}{:10_256:}{:10_256:}

hrp 发表于 2020-9-20 08:57:03

支持

昨非 发表于 2020-9-20 08:57:32

hrp 发表于 2020-9-20 08:57
支持

谢谢肯定

1336069416 发表于 2020-9-20 09:17:57

66

我很cai的 发表于 2020-9-20 10:36:11

1

疾风怪盗 发表于 2020-9-20 12:40:35

本帖最后由 疾风怪盗 于 2020-9-20 12:42 编辑

写得不错啊。。。。。。{:10_256:}
1、能不能加图形化界面。。。。。。。。
2、如果学生重名,怎么办?
3、复制了代码,运行报错
FileNotFoundError: No such file or directory: 'data.json'
是否可以增加判断,如果没有这个文件就新建一个空的

昨非 发表于 2020-9-20 12:42:04

疾风怪盗 发表于 2020-9-20 12:40
写得不错啊。。。。。。
能不能加图形化界面。。。。。。。。
考虑一个问题,如果学生重名,怎 ...

可以加的,还有排序,查找依据也不唯一。。。。
能优化的地方多了去了,
。。。。{:10_266:}
页: [1] 2 3 4 5 6 7 8 9 10
查看完整版本: 【详解】python信息管理系统【新】