学生信息管理系统v1.3 - - python
- 内容
- 对上节实现的学生信息管理系统进行重构,要求实现日志功能和异常处理功能。
- 使用系统自带库 logging 和 shelve,不引入第三方库
- 导入 sys 包,使用 sys.exit( ) 可退出程序。
- 数据要求的改变:学生的身高和体重使用数字类型存储。
- 学生信息管理系统v1.0
- 学生信息管理系统v1.1
- 学生信息管理系统v1.2
本次升级所用到的python知识
- 错误和异常
- 语法错误
Python 的语法错误或称为解析错误,经常被初学者所碰到
count = 0
while (count < 10)
print('本次的count是: ',count)
count += 1
print('count输出结束。')
运行后会出现如下错误,是因为 while 后缺少冒号(:)
- 异常
即便 Python 程序语法是正确的,在运行时也有可能出现错误,在运行时检测到的错误称为异常
print(10*(1/0))
运行时会产生如下异常,因为 0 不能作为除数
因为在程序中难免会碰到此类问题,所以在程序中必须对可能发生的错误进行处理,否则程序会因为各种问题终止并退出。
而 Python 内置了一套异常处理,来帮助我们进行错误的处理。
- 异常处理
- try…except 语句
当我们认为某些代码可能会出错时,就可以用 try 来运行这段代码,如果执行出错,则后续代码不会继续执行,而是直接跳转至错误处理代码,即 except 语句块。
以下为拦截 0 作为除数发生的异常:
try:
print('try...')
r = 10 / 0
print('result:', r)
except ZeroDivisionError as e:
print('except:', e)
finally:
print('finally...')
print('END')
运行后结果如下:
从输出结果可见,当错误发生后,后续语句 print(‘result:’, r) 不会被执行,expect 由于捕获到 ZeroDivisionError,因此被执行,然后程序继续走,执行 finally。
- Python 定义的主要异常
- BaseException # 所有异常的父类
- SystemExit # 由sys.exit()抛出的异常
- KeyboardInterrupt # 通常由ctrl+c或者Delete抛出的异常
- GeneratorExit # 当生成器被关闭时抛出的异常
- Exception #
- StopIteration # 迭代结束异常
- StopAsyncIteration # 由异步迭代的`__anext__()`抛出的异常
- ArithmeticError # 各种算数错误引起的异常
- FloatingPointError # 浮点数操作错误
- OverflowError # 结果超出范围
- ZeroDivisionError # 0为除数异常
- AssertionError # assert错误异常
- AttributeError # 属性引用异常
- BufferError # 缓存错误
- EOFError # 读不到数据
- ImportError # import错误
- ModuleNotFoundError # 找不多模块
- LookupError # 由索引和key值引起的异常
- IndexError # 索引错误
- KeyError # 字典key值错误
- MemoryError # 内存溢出异常
- NameError # 本地和全局找不到变量名
- UnboundLocalError # 局部变量没有赋值
- OSError # system错误
- BlockingIOError # 调用阻塞异常错误
- ChildProcessError # 子进程
- ConnectionError # 连接
- BrokenPipeError # 管道读写异常
- ConnectionAbortedError # 连接失败
- ConnectionRefusedError # 连接拒绝
- ConnectionResetError # 连接重置
- FileExistsError # 创建文件和文件夹错误
- FileNotFoundError # 文件未找到
- InterruptedError # 中断错误
- IsADirectoryError # 文件操作用在文件夹上
- NotADirectoryError # 不是文件夹
- PermissionError # 权限
- ProcessLookupError # 进程不存在
- TimeoutError # 超时
- ReferenceError # 引用异常
- RuntimeError #
- NotImplementedError # 运行抽象方法
- RecursionError # 超出最大递归深度
- SyntaxError # 语法错误
- IndentationError # 缩进错误
- TabError # tab错误
- SystemError # 解释器中断
- TypeError # 类型错误
- ValueError # 赋值错误
- UnicodeError #
- UnicodeEncodeError # unicode编码错误
- UnicodeDecodeError # unicode解码错误
- UnicodeTranslateError # unicode转换错误
- Warning #
- DeprecationWarning # 操作不赞成警告
- PendingDeprecationWarning # 表明此操作将来会被弃用
- UserWarning # 用于用户生成警告
- SyntaxWarning # 语法可疑警告
- RuntimeWarning # 运行警告
- FutureWarning # 将会改变警告
- ImportWarning # 导入警告
- UnicodeWarning # unicode相关警告
- BytesWarning # 字节相关警告
- ResourceWarning # 资源使用情况警告
- try 如何工作
1)首先,执行 try 子句(在关键字 try 和关键字 except 之间的语句)。
2)如果没有异常发生,忽略 except 子句,try 子句执行后结束。
3)如果在执行 try 子句的过程中发生了异常,那么 try 子句余下的部分将被忽略。如果异常的类型和 except 之后的名称相符,那么对应的 except 子句将被执行。最后执行 try 语句之后的代码。
4)如果一个异常没有与任何的 except 匹配,那么这个异常将会传递给上层的 try 中。
- 多种异常
如果发生了不同类型的错误,应该由不同的 except 语句块处理(类似于多重 if)。
try:
r = 10 / int(input('请输入: '))
except ValueError as e:
print('ValueError:', e )
except ZeroDivisionError as e:
print('ZeroDivisionError:', e)
finally:
pass
print('END')
输入 a 运行:
输入0运行:
如果没有错误发生,可以在 except 语句块后加一个 else,当没有错误发生时会自动执行 else 语句。
try:
r = 10 / int(input('请输入: '))
except ValueError as e:
print('ValueError:', e )
except ZeroDivisionError as e:
print('ZeroDivisionError:', e)
else:
print('r =', r)
finally:
pass
print('END')
输入 2 运行:
- 跨层调用
使用 try…except 捕获错误还有一个巨大的好处,就是可以跨越多层调用,比如函数 main( ) 调用 foo( ),foo( ) 调用 bar( ),结果 bar( ) 出错了,这时,只要 main( ) 捕获到就可以处理。
在异常处理机制中,Python 实现的跨层调用相对于C++、Java比较方便。
def foo(s):
return 10 / int(s)
def bar(s):
return foo(s) * 2
def main():
try:
bar('0')
except Exception as e:
print('Error:', e)
finally:
print('finally...')
if __name__ == '__main__':
main()
也就是说,不需要在每个可能出错的地方去捕获错误,只要在合适的层次去捕获错误就可以了。这样一来,就大大减少了写 try…except…finally 的麻烦。
- 记录错误
如果不捕获错误,自然可以让 Python 解释器来打印出错误堆栈,但程序也被结束了。既然我们能捕获错误,就可以把错误堆栈打印出来,然后分析错误原因,同时,让程序继续执行下去。
Python 内置的 logging 模块可以非常容易地记录错误信息:
import logging
def foo(s):
return 10 / int(s)
def bar(s):
return foo(s) * 2
try:
bar('0')
except Exception as e:
logging.exception(e)
print('END')
- 调试与日志
程序能一次写完并正常运行的概率很小,当遇到复杂 bug 时,我们需要一套调试程序的手段来修复。
logging 日志就是一种很好的处理,logging 不会抛出错误,而且可以输出到文件:
import logging
logging.basicConfig(level = logging.INFO)
s = '0'
n = int(s)
logging.info('n = %d' % n )
print(10 / n)
这就是 logging 的好处,它允许你指定记录信息的级别,有 debug,info,warning,error 等几个级别,当我们指定 level= INFO 时,logging.debug 就不起作用了,其余同理。这样就可以放心地输出不同级别的信息,也不用删除,最后统一控制输出哪个级别的信息。
- 日志书写格式
import logging
logging.basicConfig(
level=logging.INFO, #定义输出到文件的log级别
format='%(asctime)s %(filename)s %(levelname)s %(message)s', #日志格式
datefmt='%Y-%m-%d %A %H:%M:%S', #日期格式 年(大写) 月 日 星期 时 分 秒
filemode='a', #不能用W,被覆盖后查看不到之前录入信息
filename='zuoye8-1.log' #文件名
) # 设定日志
- 学生信息管理系统v1.3
代码如下:
- shelve库
# coding: utf-8
# Time: 2021/9/15 15:37
# Author: pinkward
# Software: PyCharm
# 此代码仅在系统第一次运行前执行,做数据文件的初始化
import shelve # 导入模块
studic = {
'x':{'name':'pinkward','height(cm)':'180','weight(kg)':'65','class':'x1','tel':'11111111111'}
} # 初始存储数据 子字典形式储存
f = shelve.open('stu') # stu文件
f['studic'] = studic # 存入studic字典
f['gly'] = {'admin': '123456'} # 存入gly字典
f.close()
# coding: utf-8
# Time: 2021/9/20 14:37
# Author: pinkward
# Software: PyCharm
# 学生信息管理系统v1.3
import shelve
import logging
import sys # python自己的描述环境
logging.basicConfig(
level=logging.INFO, #定义输出到文件的log级别
format='%(asctime)s %(filename)s %(levelname)s %(message)s', #日志格式
datefmt='%Y-%m-%d %A %H:%M:%S', #日期格式 年(大写) 月 日 星期 时 分 秒
filemode='a', #不能用W,被覆盖后查看不到之前录入信息
filename='zuoye8-1.log' #文件名
) # 设定日志
f = shelve.open('stu') # 从文件中读入数据
try:
studic = f['studic']
gly = f['gly'] # 读入数据 如果为空拦截报错
except KeyError as e:
print('数据文件损坏,无法使用')
logging.error('数据文件损坏,无法使用')
f.close()
sys.exit()
finally:
f.close()
def query(): # 查询函数
if len(studic):
for x in studic: # 用for循环访问studic字典
stu = studic[x] # 依据学号 将子字典赋给stu
print('--学号:', x) # 输出键
print('\t姓名: ', stu['name'])
print('\t身高(cm):', stu['height(cm)'])
print('\t体重(kg):', stu['weight(kg)'])
print('\t班级: ', stu['class'])
print('\t电话: ', stu['tel']) # 拿出子字典中的值
else:
print('---当前无学生信息!---')
def insert(): # 添加函数
xh = input('请输入学生学号: ')
if not xh or xh in studic: # 判断系统内是否有该学号
print('---此学号已存在!请重新输入:---')
else: # 学生的各项信息均为必填项
while 1:
name = input('请输入姓名: ')
if name:
break
while 1:
try:
height = int(input('请输入身高: '))
if height and (height > 50 and height < 230):
break
else:
print('身高必须在50~230之间!')
except ValueError as e:
print('身高必须是数字!')
logging.error('身高必须是数字!')
while 1:
try:
weight = int(input('请输入体重: '))
if weight and (weight > 40 and weight < 200):
break
else:
print('体重必须在40~200之间!')
except ValueError as e:
print('体重必须是数字!')
logging.error('体重必须是数字!')
while 1:
bj = input('请输入班级: ')
if bj:
break
while 1:
tel = input('请输入电话: ')
if tel:
break
studic[xh] = {'name': name, 'height(cm)': height, 'weight(kg)': weight, 'class': bj, 'tel': tel} # 存入字典
print('---已成功录入学生信息!---')
def update(): # 修改函数
xh = input('请输入学生学号: ')
if xh and xh not in studic: # 判断系统内是否有该学号
print('---此学号不存在!请重新输入:---')
else:
while 1:
name = input('请输入姓名: ')
if name:
break
while 1:
try:
height = int(input('请输入身高: '))
if height and (height > 50 and height < 230):
break
else:
print('身高必须在50~230之间!')
except ValueError as e:
print('身高必须是数字!')
logging.error('身高必须是数字!')
while 1:
try:
weight = int(input('请输入体重: '))
if weight and (weight > 40 and weight < 200):
break
else:
print('体重必须在40~200之间!')
except ValueError as e:
print('体重必须是数字!')
logging.error('体重必须是数字!')
while 1:
bj = input('请输入班级: ')
if bj:
break
while 1:
tel = input('请输入电话: ')
if tel:
break
studic[xh] = {'name': name, 'height(cm)': height, 'weight(kg)': weight, 'class': bj, 'tel': tel} # 存入字典
print('---已成功修改学生信息!---')
def delete(): # 删除函数
xh = input('请输入学生学号: ')
if xh not in studic: # 判断系统是否有该学号
print('---此学号不存在!请重新输入:---')
else:
del studic[xh] # 删除键即删除值 实现删除信息功能
print('---成功删除该学号学生信息!---')
def editmanager(): # 维护函数
while 1:
mname = input('请输入管理员用户名:')
if mname:
break
while 1:
mpwd1 = input('请输入管理员密码: ')
if mpwd1:
break
while 1:
mpwd2 = input('请再次输入管理员密码: ')
if mpwd2:
break
if mname and mpwd2 == mpwd1:
gly.clear()
gly[mname] = mpwd2
print('---维护管理员信息成功!---')
else:
print('---用户名或密码错误!---')
def main(): # main函数
print()
print('欢迎使用学生信息管理系统v1.3!'.center(50))
username = input('请输入用户名: ')
password = input('请输入密码: ')
if username in gly and password == gly[username]:
while 1:
while 1:
print()
print('学生信息管理系统v1.3'.center(50))
print('系统菜单'.center(52))
print('1.添加学生信息'.center(50))
print('2.删除学生信息'.center(50))
print('3.修改学生信息'.center(50))
print('4.查看学生信息'.center(50))
print('5.维护管理员信息'.center(52))
print('6.退出管理系统'.center(50))
opt = input('请选择功能: ')
if not opt or opt not in '123456' or len(opt) > 1:
print('---无效选项!请重新选择:---')
continue
else:
break # 确保正确输入进入功能选项
if opt in '1':
insert() # 调用添加函数
elif opt in '2':
delete() # 调用删除函数
elif opt in '3':
update() # 调用修改函数
elif opt in '4':
query() # 调用查询函数
elif opt in '5':
editmanager() # 调用维护函数
else:
f = shelve.open('stu') # 将现有信息写入文件
f['studic'] = studic
f['gly'] = gly # 存入当前信息
f.close()
break # 退出管理系统
print('---已成功退出!---')
print('感谢使用学生信息管理系统v1.3!'.center(50))
else:
print('---普通用户登录成功!---') # 普通用户页面
while 1:
while 1:
print()
print('欢迎使用学生信息管理系统v1.3!'.center(50))
print('系统菜单'.center(52))
print('4.查看学生信息'.center(50))
print('5.退出管理系统'.center(50))
opt = input('请选择功能: ')
if not opt or opt not in '45' or len(opt) > 1:
print('---无效选项!请重新选择:---')
continue
else:
break # 确保正确输入进入功能选项
if opt in '4':
query() # 调用查询函数
else:
break # 退出管理系统
print('---已成功退出!---')
print('感谢使用学生信息管理系统v1.3!'.center(50))
if __name__ == '__main__': # 判断是否为自主运行
main() # 调用main函数