Python期末作业智能停车管理系统:从需求拆解到JSON持久化与flet界面完整指南

发布时间:2026/9/23 14:33:11
Python期末作业智能停车管理系统:从需求拆解到JSON持久化与flet界面完整指南 简介一份用Python编写的智能停车管理系统模拟出入场项目面向Python期末作业、课程设计或K12阶段进阶练习可完整演示车辆入场、出场计费到信息持久化的过程。系统采用OpenCV实现车牌识别结合YOLOOCR深度学习方案利用MySQL完成停车信息存储与计费查询并通过pygame搭建用户交互界面在业务流程上涵盖入场识别、出场计费、车位信息更新、数据库写入等环节同时完成可行性分析、需求分析、概要设计、详细设计到软件测试的完整开发流程。压缩包共十二个文件包含八个Python脚本、三个keep占位文件及一个说明txt大小仅17KB脚本按数据库、车牌识别、计费、主程序等模块拆分便于阅读与复用适合在课程设计中参考。目前已有762人学习浏览说明该方案具备一定参考价值。读者可从中获取完整代码框架、GUI设计思路及计费算法实现并可根据项目文档快速梳理E-R模型与模块职责直接作为期末作业模板或二次开发基础。1. Python期末作业做智能停车管理系统为什么我劝你别一上来就写代码期末作业里“智能停车管理系统模拟出入场”算是个高频题目Word 版需求翻来覆去就是那几句能模拟车辆入场、出场显示剩余车位按停车时长计费。听起来不难但每年都有人在这个题目上翻车——不是逻辑写不出来而是把项目做成了一个“一次性脚本”运行、输入、出结果、关掉数据全丢代码也只有一百行答辩时老师问“你用了什么数据结构”“数据怎么持久化的”直接卡壳。这个题目的本质不在于“停车”业务有多复杂而在于用 Python 把一套完整的小型业务流程走通状态管理、时间计算、计费规则、数据保存、交互界面。它适合 Python 基础语法学完、想用面向对象和文件操作串一个综合项目的初学者也是后续做 Web 版或图形界面版停车系统的地基。本篇就按我实际带过的课程设计思路把这条路从头拆到尾顺便把那些让人折腾到半夜的坑提前指出来。2. 先把需求拆干净这个系统到底在模拟什么拿到题目别急着开 IDE。先花半小时把“模拟出入场”五个字翻译成程序里的功能点这一步决定了后面类的设计、方法划分和数据存法。很多人的期末项目之所以代码越写越乱就是因为在需求还没理清的时候就开始写输入输出。2.1 从一句话需求到功能清单“模拟出入场”至少包含五件事车位有总数和剩余数车辆入场要登记并占用一个车位车辆出场要释放车位按停车时长收费中间任何时刻能查询当前车位占用情况。把这几件事再细化成可验收的交互动作就得到下面这张功能清单。模块功能点对应期末考核点入场登记输入车牌记录入场时间剩余车位减一输入处理、时间获取、状态更新出场结算输入车牌查找入场记录计算时长并计费剩余车位加一字典查找、时间计算、分支判断车位查询显示总车位、剩余车位、当前在场车辆列表数据遍历、格式化输出计费规则按白天/夜间不同单价或统一单价计算函数封装、参数设计数据持久化程序重启后仍能恢复停车记录文件读写、JSON 序列化注意这里有一个容易被忽略的点查询功能是“随时可查”这意味着系统里必须有一份“在场车辆”的实时状态而不是等到出场时才去翻历史文件。很多初版代码把记录存在本地列表里跑一次程序没问题重启后查不到任何在场车辆根因就是把“内存状态”和“持久化数据”混为一谈了。2.2 用类还是用字典期末答辩视角下的选择用纯字典写这个系统完全跑得通比如cars {京A12345: {入场时间: 1699000000}}但期末项目讲究的是“展示学习成果”。用类封装能让代码结构更清晰也自然覆盖了“面向对象”这个考点。我一般建议把系统拆成两个核心类一个描述车辆一个描述停车场。Car类记录车牌和入场时间戳也可以预留一个备注字段方便后续扩展会员标记。ParkingLot类管理总车位数、在场车辆字典、计费单价等提供入场、出场、查询、保存、加载方法。对比一下两种方案纯字典实现起来代码量少但出场结算、数据校验、格式统一这些逻辑全散落在函数里用类实现每个职责落进方法内部答辩时老师问“车辆数据存在哪”你直接指Car类的字段就能说清楚。从课程设计的角度类方案不仅更好写也更好讲。2.3 先搭骨架ParkingLot 类的字段与初始化逻辑类的字段设计决定了后续所有方法的复杂度。我的建议是字段宁多勿缺但不要提前做“可能未来会用到”的过度设计。下面这个骨架足够应付出入场、计费和持久化三件事。import json import time from typing import Dict, Optional class Car: 车辆实体类保存一辆车在停车场内的关键信息 def __init__(self, plate: str, enter_time: float): self.plate plate # 车牌号统一大写去除首尾空格 self.enter_time enter_time # 入场时间戳float 类型 class ParkingLot: def __init__(self, total_spaces: int, day_price: float, night_price: float): self.total_spaces total_spaces # 总车位数 self.cars: Dict[str, Car] {} # 在场车辆字典key 为车牌 self.day_price day_price # 白天单价元/小时 self.night_price night_price # 夜间单价元/小时 property def available_spaces(self) - int: 剩余车位总数减去在场车辆数 return self.total_spaces - len(self.cars)这段代码有几个值得在答辩时讲的细节。cars字典用plate做 key出场时直接self.cars.get(plate)就能拿到记录时间复杂度是 O(1)比遍历列表快得多。available_spaces用property装饰成属性调用时不用加括号语义上更像“一个状态”而不是“一个动作”。车牌统一在入口处做格式化后面所有查询、比对都不会因为大小写或首尾空格翻车。3. 出入场核心逻辑从入场登记到离场计费骨架搭好后核心就是入场和出场这两个方法。这两个方法写好了系统的主流程就通了。注意计费部分不要和业务逻辑混在一起单独抽一个方法出来方便改价格规则。3.1 入场流程先查容量再登记车辆入场方法要做三件事检查是否还有空车位检查车牌是否已经入场通过校验后写入cars字典。这三步顺序不能乱——如果先把车加进去再检查容量满位时就会出现负数空位。def enter(self, plate: str) - bool: 车辆入场登记成功返回 True失败返回 False plate plate.strip().upper() # 去空格、转大写统一格式 if not plate: print(车牌号不能为空) return False if len(self.cars) self.total_spaces: print(停车场已满无法入场) return False if plate in self.cars: print(f车牌 {plate} 已在停车场内请勿重复入场) return False self.cars[plate] Car(plate, time.time()) # time.time() 返回当前时间戳 print(f车辆 {plate} 入场成功剩余车位 {self.available_spaces}) return Truetime.time()返回的是自 1970 年 1 月 1 日以来的秒数也就是 Unix 时间戳。有人喜欢用datetime.now()存字符串但时间戳的优点是计算时长直接用减法格式化展示时再转换存储和计算分离。对期末作业来说这个习惯能帮你在出场计费时少掉一半的字符串解析代码。重复入场检查看起来多余但实际操作中手误重复输入同一车牌非常常见这个判断能避免“同一辆车占了两个车位”的尴尬局面。3.2 出场流程查找记录、计算时长、按规则计费出场方法比入场多两个步骤按车牌找到入场记录计算停车时长并计费。最关键的边界是“查不到记录”——如果用户输入了一个没入场的车牌程序必须给出提示而不是抛异常。def leave(self, plate: str) - Optional[float]: 车辆出场结算返回停车费用若车辆不在场返回 None plate plate.strip().upper() car self.cars.get(plate) if car is None: print(f未找到车牌 {plate} 的入场记录) return None stay_seconds time.time() - car.enter_time stay_hours stay_seconds / 3600 fee self.calc_fee(stay_hours) del self.cars[plate] print(f车辆 {plate} 停车 {stay_hours:.2f} 小时费用 {fee:.2f} 元) print(f出场成功剩余车位 {self.available_spaces}) return feeself.cars.get(plate)用.get()而不是self.cars[plate]区别在于前者在 key 不存在时返回None后者直接抛KeyError。对用户输入类程序永远优先用.get()。stay_hours是浮点数保留两位小数展示但参与计费时用原始精度避免多次四舍五入把账算错。del self.cars[plate]放在所有计算之后保证即使计费函数报错车辆记录也不会被误删。3.3 计费参数怎么设白天夜间双单价与最小计费单元计费规则是评分的重头戏也是最能体现“可配置性”的地方。我见过的大部分参考实现都是单一价格乘时长但稍微升级一点的做法是把一天拆成白天和夜间两段价格不同。这里的关键不是规则多复杂而是参数要暴露出来不能写死在计算逻辑里。def calc_fee(self, stay_hours: float) - float: 计费逻辑白天按 day_price夜间按 night_price不足一小时按一小时计 if stay_hours 0: return 0.0 whole_hours math.ceil(stay_hours) # 向上取整不足 1 小时按 1 小时 fee 0.0 for i in range(whole_hours): hour_time (self.car_enter_hour i) % 24 # 需要传入入场小时数 if 8 hour_time 20: fee self.day_price else: fee self.night_price return fee这个版本的计费逻辑有一个隐藏问题self.car_enter_hour没有定义需要把入场时刻的小时数传进来。常见的修正方案是修改函数签名为calc_fee(self, stay_hours: float, enter_hour: int)然后按“每整小时判断一次该小时属于白天还是夜间”来累加。比如入场是 19:30停了 2.5 小时系统会按 3 小时算其中 19:30-20:00 这不足一小时的 30 分钟按夜间算还是按小时单价算不同老师对规则的理解可能不同。我的建议是在代码注释里写明“费用按整小时向上取整不足一小时按一小时计”老师问起时你能答得有理有据比规则本身更重要。4. 把数据存下来JSON 持久化与最小图形界面做完出入场逻辑系统已经能跑但关掉程序再打开所有在场车辆全没了。这样交上去功能上说得通工程上站不住脚。接下来这一步是把“运行期数据”变成“文件里的数据”再决定要不要加个界面。4.1 数据存 JSON 还是 txt对比与选择Python 写文件有两种常见选择json.dump序列化字典或者按固定分隔符写文本。对课程设计这个规模JSON 几乎是唯一合理的答案——结构清晰、自带层级关系、json.load直接还原成字典省去自己写解析逻辑的麻烦。用 CSV 存储也行但 csv 模块的读写需要处理表头和字段顺序代码量明显增加。我一般建议这样做每次入场、出场操作成功后立即把当前cars字典写到 JSON 文件。不要等程序退出时统一保存因为如果程序在退出前崩溃数据还是会丢。def save_to_file(self, filename: str parking_data.json) - None: 把当前在场车辆数据保存到 JSON 文件 data { total_spaces: self.total_spaces, cars: { plate: car.enter_time for plate, car in self.cars.items() } } with open(filename, w, encodingutf-8) as f: json.dump(data, f, ensure_asciiFalse, indent2)这里有个细节ensure_asciiFalse保证中文车牌存储后以明文形式出现在 JSON 里而不是转成\u编码后面调试时直接用文本编辑器打开就能看到内容。indent2把 JSON 排成带缩进的格式方便肉眼检查。with open语句确保文件写入完成后自动关闭异常时也不会残留文件句柄。加载文件的方法逻辑刚好相反注意处理文件不存在的情况。第一次运行程序时文件还没生成直接open会抛FileNotFoundError所以要先用os.path.exists判断。4.2 环境准备pycharm 或 vscode 配置 python 环境与第三方库安装如果是纯控制台版只需要 Python 标准库不需要额外安装任何第三方库。但如果你想把界面做成图形版本常见选择是flet——它基于 Flutter 的渲染引擎用纯 Python 写界面且能直接打包成桌面应用对期末项目来说投入产出比很高。这里先说清楚环境配置避免卡在第一步。在 pycharm 里新建项目时选择已存在的解释器或者在设置里找到 Python Interpreter 添加这是最常用的方式。如果用 vscode需要先装 Python 扩展再用python -m pip install flet安装第三方库。注意用python -m pip而不是直接pip因为后者可能会装到系统的 Python 而不是当前激活的虚拟环境中。python -m pip install flet装完后在 Python 交互环境里执行import flet不报错就说明环境就绪。flet 的版本更新比较快不同版本的部分参数名有调整如果发现示例代码报错优先去查你当前版本的 API 变化。4.3 用 flet 搭一个最小可交互界面控制台版的逻辑已经完整图形界面版的关键是把之前写好的enter、leave、查询三个方法接到按钮事件上。下面这个最小示例展示了 flet 的基本用法页面由文本框、按钮和列表组成点击按钮时调用ParkingLot的方法并刷新界面。import flet as ft from parking_lot import ParkingLot lot ParkingLot(total_spaces50, day_price5.0, night_price2.0) def main(page: ft.Page): page.title 智能停车管理系统 plate_input ft.TextField(label车牌号) info_text ft.Text(剩余车位50) def on_enter(e): plate plate_input.value result lot.enter(plate) info_text.value f剩余车位{lot.available_spaces}最近操作{result} page.update() def on_leave(e): plate plate_input.value fee lot.leave(plate) if fee is not None: info_text.value f剩余车位{lot.available_spaces}本次收费{fee:.2f} 元 page.update() page.add( plate_input, ft.Row([ ft.ElevatedButton(入场, on_clickon_enter), ft.ElevatedButton(出场, on_clickon_leave), ]), info_text ) ft.app(targetmain)ft.TextField的value是用户输入的内容点击按钮后通过事件函数读取并交给业务逻辑处理。页面组件需要用page.update()手动刷新让界面上显示最新数据。这个版本把“业务逻辑”和“界面显示”分开ParkingLot里的方法只负责数据处理界面上只管调用和展示。答辩时老师如果问“界面和逻辑怎么解耦”你可以直接指着这两个文件说明。不想用图形界面时控制台版用input()也能完成同样功能直接在主文件里写一个while True循环根据用户输入的分支调用对应方法即可。5. 避坑期末项目最常翻车的 6 个细节做课程设计最怕的不是功能写不出来而是写完了却在演示、答辩时翻车。下面这些坑我几乎每年都见到学生踩一遍提前排掉能省下大量改代码的时间。5.1 车牌大小写不一致导致“找不到入场记录”现象入场时输入京A12345出场时输入京a12345系统提示找不到记录。原因用户输入的车牌大小写不一致而字典的 key 是严格区分大小写的。解决方法在enter和leave方法中统一调用plate.strip().upper()强制把车牌转成大写再存储和查找。这一步要放在所有逻辑之前而不是只在某一个方法里做一旦某个入口漏了演示时就会当场翻车。5.2 程序退出后数据全丢现象程序正常运行所有操作都成功但关闭后重新打开剩余车位恢复满员在场车辆列表为空。原因数据只存在内存里的cars字典中没有写盘。解决方法把save_to_file方法在enter、leave各成功分支后各调用一次。不要用“退出时统一保存”的方案因为exit()之前的代码可能被异常打断。用 try/finally 包住主循环也行但最稳的做法是“每次变更立即保存”。5.3 计费出现负数现象停车 2 小时费用显示为负。原因系统当前时间小于入场记录的时间戳或者手动修改了系统时间导致时差异常。解决方法在calc_fee开头加一行stay_hours max(0, stay_hours)把负值钳制为零。千万别在和老师演示的时候手动调系统时间玩“穿越停车”这个坑是自己给自己挖的。5.4 满位判断与车辆登记顺序写反现象车位只剩 1 个时连续入场 2 辆车第二辆也显示成功。原因代码先执行了self.cars[plate] Car(...)再判断len(self.cars) self.total_spaces导致满位判断永远晚一步。解决方法严格按“先判断、后写入”的顺序执行。这类逻辑错误的特征是极难在功能测试中暴露只有并发或连续操作时才会显现但期末项目里数据量小思路清晰比压测更重要。5.5 文件加载后类属性缺失现象写好了load_from_file方法但加载后调用available_spaces报错。原因加载方法只还原了cars字典没有还原total_spaces和单价属性对象处于“半初始化”状态。解决方法加载文件后执行一次完整的初始化流程可以写一个类方法直接从 JSON 构建对象避免手动拼凑属性。5.6 图表库环境冲突导致演示崩溃现象为做可视化安装了多个第三方库答辩现场打开项目运行时报 ImportError 或版本冲突。原因开发过程中陆续装了不同版本的库本机正常换台机器就挂。解决方法在项目根目录生成requirements.txt答辩前换台干净机器测试一遍运行。生成命令python -m pip freeze requirements.txt新环境恢复时执行python -m pip install -r requirements.txt。这是工程化习惯但它能直接避免答辩现场最大的尴尬。6. 验收清单与演示技巧让老师和面试官一眼看到工作量期末项目交的不只是代码还有演示过程和验收效果。一套好的演示逻辑能把代码里那些隐藏的“工程感”暴露出来。建议按下述顺序演示先运行程序展示当前剩余车位入场一辆车展示车牌格式化处理和空位变化查询停车场状态展示字典遍历出场结算展示计费过程关闭程序重新打开展示数据恢复证明持久化没白做。整个过程控制在 3 分钟内每个步骤对应一个考核点老师问什么你都能接得住。演示步骤对应功能点演示时说的重点入场 2 辆车容量管理、车牌格式化“入场时车牌自动转大写剩余车位实时减少”查询在场车辆字典遍历“用字典存储查车时间复杂度 O(1)”出场 1 辆车时长计算、计费规则“按小时向上取整白天夜间不同单价”重启程序持久化验证“每次操作实时写 JSON 文件重启后数据还在”老师大概率会问的三个问题为什么用字典而不用列表存车辆答案通过车牌直接定位记录更高效。时间戳为什么用 float 而不是字符串答案时长计算直接做差格式化输出时再转为可读时间。如果扩容到 1000 个车位程序哪部分需要改答案容量字段的值其余逻辑无需调整。前两个问题请从代码里找答案第三个问题其实暗示了类的参数化设计是合理的。你还可以给系统留一个扩展口子在Car类里增加一个is_member字段出场计费时会员打八折。这个功能的代码量不到十行但能让程序看起来具备“业务成长性”。如果还有余力可以顺带提一句“后续接入车牌摄像头识别后只需要替换入场时的车牌输入方式”但不要真去接 cv2 做图像识别那属于另一个课题的体量。最后想说的是期末项目最忌贪多把出入场链路做扎实数据落盘做干净演示有逻辑这已经足够拿一个体面的分数。整个流程里我最深的体会是在动手前把“车辆状态怎么保存、什么时候同步到文件”想清楚比后面补任何代码都省时间。希望帮到你。本文还有配套的精品资源点击获取