基于MySQL课程时间的Python考勤系统循环检测逻辑优化问题
基于MySQL课程时间的Python考勤系统循环检测逻辑优化问题
看起来你已经把核心的逻辑搭起来了,问题就出在那个break语句上——它让脚本在没检测到课程时直接退出,而不是持续等待检测。我来一步步帮你解决这个问题,从修复基础循环到进阶的调度模式:
一、先修复基础循环:持续轮询不退出
最直接的解决方法是把break替换成延迟等待后继续循环,这样脚本会一直运行,每隔一段时间检查一次当前是否有课程,而不是直接退出。
修改后的核心循环代码
import time # (前面的数据库查询、lectures列表构建、get_current_lecture函数保持不变) while True: current = get_current_lecture() if current is None: # 没有课程时,等待一段时间再重新检测,避免占用过多CPU print("当前无课程,等待中...") time.sleep(30) # 每30秒检查一次,可根据需求调整 else: print(f"检测到当前课程,开始人脸识别,直到 {current[1]}") run_face_detection_until(current[1]) # 人脸识别结束后,继续循环检测下一节课程
关键说明:
- 去掉
break,换成time.sleep():这样脚本在空闲时会进入短暂休眠,不会疯狂占用CPU资源,同时每隔固定时间重新检查时间。 - 休眠时长可以根据需求调整:比如上课前可以设短一点(比如10秒),非上课时段设长一点(比如5分钟),甚至可以根据下一节课程的时间动态计算休眠时长(后面会讲这个进阶优化)。
二、进阶优化:动态计算休眠时长(减少不必要的轮询)
上面的固定休眠可能不够高效,比如离下一节上课还有1小时,没必要每30秒查一次。我们可以提前计算下一节课程的开始时间,然后直接休眠到那个时间点附近,再开始检测。
新增函数:获取下一节课程的开始时间
def get_next_lecture_start(): now = datetime.now() # 筛选出所有还未开始的课程 upcoming_lectures = [start for start, end in lectures if start > now] if not upcoming_lectures: # 当天没有后续课程了,可以考虑休眠到第二天,或者继续固定轮询 return None # 返回最近的下一节课程开始时间 return min(upcoming_lectures)
优化后的循环
while True: current = get_current_lecture() if current is None: next_start = get_next_lecture_start() if next_start is None: # 当天没有后续课程,比如已经到了最后一节课结束后 print("当天课程已全部结束,10分钟后重新检查...") time.sleep(600) # 10分钟后再查,避免死等 else: # 计算距离下节课的时间,提前10秒开始准备 wait_seconds = (next_start - datetime.now()).total_seconds() - 10 if wait_seconds > 0: print(f"距离下节课还有 {wait_seconds:.0f} 秒,等待中...") time.sleep(wait_seconds) # 等待结束后,再进入下一次循环检测 else: print(f"检测到当前课程,开始人脸识别,直到 {current[1]}") run_face_detection_until(current[1])
这个优化能大幅减少不必要的轮询次数,让脚本更高效。
三、进阶方案:使用调度框架替代手动轮询
如果你的系统需要更复杂的时间调度(比如支持课程表动态更新、多任务调度),可以考虑用专业的调度库,比如APScheduler,它能更优雅地处理基于时间的任务触发,不需要自己写轮询逻辑。
用APScheduler实现的核心逻辑示例
from apscheduler.schedulers.background import BackgroundScheduler # 1. 初始化调度器 scheduler = BackgroundScheduler() # 2. 定义任务:当课程开始时启动人脸识别 def start_lecture_task(end_time): print(f"课程开始,启动人脸识别直到 {end_time}") run_face_detection_until(end_time) # 3. 从数据库加载课程时间,添加到调度器 for start, end in lectures: # 在课程开始时间触发任务 scheduler.add_job( start_lecture_task, 'date', run_date=start, args=[end] ) # 4. 启动调度器,脚本会持续运行 scheduler.start() print("考勤系统已启动,等待课程开始...") # 保持主进程运行 try: while True: time.sleep(60) except (KeyboardInterrupt, SystemExit): scheduler.shutdown()
优势:
- 不需要自己写轮询逻辑,调度器会自动处理时间触发
- 支持动态添加/移除任务(比如数据库课程表更新后,你可以重新加载任务)
- 支持多种调度模式(日期、间隔、 cron 表达式等)
四、额外建议:定时刷新数据库课程表
如果你的课程表可能在运行过程中更新(比如临时调课),建议在循环中定时重新从数据库拉取最新的课程时间,避免一直使用旧数据:
def refresh_lectures(): # 重新从数据库查询并构建lectures列表 cursor.execute("SELECT start, end FROM timetable WHERE day=%s", (datetime.today().strftime("%a"),)) rows = cursor.fetchall() lectures = [] for s, e in rows: start = datetime.combine(date.today(), s) end = datetime.combine(date.today(), e) lectures.append((start, end)) return lectures # 在循环中每隔1小时刷新一次课程表 last_refresh_time = datetime.now() while True: # 检查是否需要刷新课程表 if (datetime.now() - last_refresh_time).total_seconds() > 3600: print("刷新课程表...") lectures = refresh_lectures() last_refresh_time = datetime.now() # 后面的检测逻辑和之前一样 current = get_current_lecture() # ...
总结
- 快速修复:把
break换成time.sleep()+继续循环,就能解决脚本直接退出的问题 - 高效优化:动态计算休眠时长,减少不必要的轮询
- 长期方案:用APScheduler这类调度框架,更适合复杂的时间触发场景
- 额外保障:定时刷新数据库课程表,适配课程变动
这样你的脚本就能持续运行,空闲时等待,到时间自动启动人脸识别啦!




