You need to enable JavaScript to run this app.
最新活动
大模型
产品
解决方案
定价
生态与合作
支持与服务
开发者
了解我们

基于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])
        # 人脸识别结束后,继续循环检测下一节课程

关键说明:

  1. 去掉break,换成time.sleep():这样脚本在空闲时会进入短暂休眠,不会疯狂占用CPU资源,同时每隔固定时间重新检查时间。
  2. 休眠时长可以根据需求调整:比如上课前可以设短一点(比如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()
    # ...

总结

  1. 快速修复:把break换成time.sleep()+继续循环,就能解决脚本直接退出的问题
  2. 高效优化:动态计算休眠时长,减少不必要的轮询
  3. 长期方案:用APScheduler这类调度框架,更适合复杂的时间触发场景
  4. 额外保障:定时刷新数据库课程表,适配课程变动

这样你的脚本就能持续运行,空闲时等待,到时间自动启动人脸识别啦!

火山引擎 最新活动