AI编程(二十一):倒计时提醒器 — 教会程序看表

作者:

在

煮面计时5分钟,开会前10分钟提醒——这次用上全系列唯一的新东西:time模块

一、你缺的不是时间管理,是一个帮你盯时间的人

场景一:煮面条,说好煮5分钟。你去刷手机,一抬头——20分钟过去了,面糊了。

场景二:下午3点开会,你想提前10分钟准备。结果一忙起来抬头看表——3点05了。

场景三:敷面膜15分钟、煮鸡蛋8分钟、烤箱25分钟、午睡20分钟……

生活里到处是”X分钟之后提醒我”的需求。手机上有倒计时App,但今天我们要自己写一个。因为写这个程序会用到一个新东西——time模块。说是新东西,其实只有两个函数,5分钟就能学会。

学会之后,这个提醒器是你自己的:想怎么改就怎么改。多个计时器、声音提醒、显示剩余时间,全按你的习惯来。

二、time模块:就两个函数

Python自带一个叫time的模块(第十一篇说的”工具箱”),跟时间有关的基础功能都在里面。我们只需要两个:

第一个:time.sleep(秒数) —— 让程序睡一会儿

import time

print("我要休息3秒……")
time.sleep(3)          # 程序在这里暂停3秒,什么都不干
print("3秒到了!")

time.sleep(3)就是”程序暂停3秒”。这3秒里程序完全不动,时间到了才继续往下走。煮面计时的本质,不就是”暂停5分钟,然后喊一声”吗?

第二个:time.time() —— 问一下现在几点了

现在 = time.time()
print(现在)    # 输出类似 1737024000.0

这不是给人看的时间,而是”从1970年1月1日0点到现在过了多少秒”——一个巨大的数字。它不重要也不好看,但可以做减法:

开始 = time.time()
# ... 做一些事情 ...
结束 = time.time()
print(f"这件事花了{结束 - 开始:.1f}秒")

两个时间一减,就知道过了多久。计时器的原理就这么朴素。

三、先跟AI聊

你的提示词:

用Python写一个倒计时提醒器。用户输入分钟数,程序开始倒计时,时间到之后打印”时间到!”提醒。使用time模块的time.sleep函数,循环每秒递减,用print显示剩余时间。中文界面,代码加详细注释。

四、基础版:能用的倒计时

import time

print("=== 倒计时提醒器 ===")

# 获取倒计时时长
try:
    分钟 = int(input("倒计时几分钟:"))
except ValueError:
    print("请输入数字!")
    exit()          # 直接退出程序

# 换算成总秒数
总秒数 = 分钟 * 60
print(f"好的,{分钟}分钟后提醒你!n")

# 倒计时主循环
剩余 = 总秒数
while 剩余 > 0:
    print(f"r⏰ 剩余 {剩余 // 60:02d}:{剩余 % 60:02d}", end="")
    time.sleep(1)      # 每次暂停1秒
    剩余 -= 1          # 剩余秒数减1

# 循环结束 = 时间到
print("n🔔 时间到!时间到!时间到!")

五、逐行理解

5.1 输入处理

try:
    分钟 = int(input("倒计时几分钟:"))
except ValueError:
    print("请输入数字!")
    exit()

老朋友了:try/except处理非法输入。exit()是新面孔——它的意思是”程序到此结束”。输错了就别往下走了,直接关机走人。

5.2 秒数换算

总秒数 = 分钟 * 60

time模块只认秒,不认分钟。所以5分钟要换算成300秒再交给它。单位要搞对——第二十篇单位转换器刚练过这个。

5.3 倒计时循环

剩余 = 总秒数
while 剩余 > 0:
    print(f"r⏰ 剩余 {剩余 // 60:02d}:{剩余 % 60:02d}", end="")
    time.sleep(1)
    剩余 -= 1

循环每转一圈 = 过了1秒:

  1. 显示:打印剩余时间
  2. 睡:time.sleep(1)暂停1秒
  3. 减:剩余秒数减1
  4. 回到第1步,直到剩余 > 0不成立

这行显示代码信息量很大,拆开看:

r(回车符):光标回到本行开头。下次print会覆盖这次的内容——所以数字是”原地刷新”的,不是一行行往下滚。很多命令行进度条都靠这招。

end="":print默认结尾换行,end=""把换行去掉,这样刷新才在同一行生效。

剩余 // 60:整除(第六篇提过),总秒数里有多少个完整的分钟。300秒 // 60 = 5分钟。

剩余 % 60:取余数,除完分钟剩下的零头秒数。305秒 % 60 = 5秒。

:02d:格式化,数字占2位,不足补0。5显示成05。所以305秒显示成05:05——和电子表一个样式。

//和%这对组合,就是”分钟:秒钟”的转换公式,以后做倒计时、做时钟显示都用得上。

5.4 时间到

print("n🔔 时间到!时间到!时间到!")

循环结束,说明剩余秒数减到0了。前面加个n换行,免得接在倒计时那行屁股后面。

六、运行试试

python 倒计时提醒器.py
倒计时几分钟:1
好的,1分钟后提醒你!

⏰ 剩余 00:01
🔔 时间到!时间到!时间到!

看着屏幕上数字每秒跳一格,还挺有仪式感的。煮面试一次,真的好用。

但用几天你就会觉得差点意思:

  • 等待的时候干不了别的?(其实可以,切走就行,程序在后台跑)
  • 提醒只有一行字,忙着呢根本注意不到
  • 一次只能跑一个,煮面的时候还想烤东西怎么办?

一个一个来。

七、第一个改进:声音提醒 — 把人叫醒

光打印一行字,埋头干活的人根本看不见。得响。

终端里有个历史悠久的骚操作:打印转义字符a,它会触发终端响铃(ASCII码7,叫”BEL”字符):

print("a")

在大多数终端里,这会”哔”一声(有的终端会闪屏)。虽然是上世纪70年代传下来的老古董,但简单有效。升级版提醒:

def 响亮提醒(消息):
    """时间到时的提醒:响铃+醒目文字"""
    print("n")                       # 先空两行,让提醒更显眼
    for i in range(5):                # 响5声
        print("🔔 " + 消息)
        print("a", end="", flush=True)   # 响铃
        time.sleep(0.5)               # 两声之间隔半秒

两个细节:

  • for i in range(5):循环5次。第六篇学的。响一声容易被忽略,连响5声跑不掉。
  • flush=True:强制立刻输出。有些环境下print的内容会先攒着(缓冲),加这个参数保证铃声立即触发,不会攒到最后一起响。

如果你的终端不响(很多现代终端默认静音),还可以用系统命令:

import os
os.system('printf "\a" > /dev/tty')    # Linux/Mac

或者干脆调用系统自带的提示音程序。不过这些属于”锦上添花”,a够用了。

八、第二个改进:多个计时器 — 煮面和烤箱同时跑

做饭经常是几个计时同时进行:面条5分钟、烤箱25分钟、汤40分钟。一个个排队等可不行,得记录多个计时器,每秒检查一次谁到点。

这个设计变了:不再是”睡一大觉等一个”,而是每秒醒来一次,把所有计时器检查一遍。

import time

计时器列表 = []    # 每个元素是 {"名称": ..., "剩余秒数": ...}

def 添加计时器():
    """添加一个新的计时器"""
    名称 = input("计时器名称(如:煮面):").strip()
    if not 名称:
        print("❌ 名称不能为空")
        return
    try:
        分钟 = int(input("几分钟:"))
    except ValueError:
        print("❌ 请输入数字")
        return
    if 分钟 <= 0:
        print("❌ 时间要大于0")
        return

    计时器列表.append({"名称": 名称, "剩余秒数": 分钟 * 60})
    print(f"✅ 已添加:{名称}({分钟}分钟)")

def 显示状态():
    """显示所有计时器的剩余时间"""
    if not 计时器列表:
        print("📭 没有正在运行的计时器")
        return
    print("n" + "-" * 36)
    for t in 计时器列表:
        秒 = t["剩余秒数"]
        print(f"  ⏰ {t['名称']:<10} 剩余 {秒 // 60:02d}:{秒 % 60:02d}")
    print("-" * 36)

def 主循环():
    """每秒检查一次所有计时器"""
    print("=== 多功能倒计时提醒器 ===")
    print("提示:先添加计时器,然后程序开始统一倒计时n")

    while True:
        显示状态()
        命令 = input("输入名称添加计时器,输入go开始倒计时:").strip()
        if 命令 == "go":
            if not 计时器列表:
                print("至少添加一个计时器!")
                continue
            break
        elif 命令:
            # 输入的不是go,当作名称?这里简化:直接调添加
            添加计时器()

    # ===== 倒计时阶段 =====
    while 计时器列表:                # 只要还有计时器就继续
        time.sleep(1)                # 每秒醒一次

        到点的 = []                  # 记录这秒谁到点了
        for t in 计时器列表:
            t["剩余秒数"] -= 1
            if t["剩余秒数"] <= 0:
                到点的.append(t)

        # 把到点的提醒一遍,然后从列表里移除
        for t in 到点的:
            响亮提醒(f"{t['名称']}时间到!")
            计时器列表.remove(t)     # 从列表删除这个计时器

        # 剩下的显示一眼
        if 计时器列表:
            简报 = " | ".join(f"{t['名称']}{t['剩余秒数'] // 60}:{t['剩余秒数'] % 60:02d}"
                          for t in 计时器列表)
            print(f"r⏳ {简报}", end="")

    print("n所有计时器都结束啦!")

主循环()

看着代码变长了,但每一行都是你认识的东西:

  • 计时器列表:列表装字典——第十七篇成绩管理器的同款数据结构。一个计时器=一个字典,两个键:名称、剩余秒数。
  • 每秒检查一遍所有计时器:for t in 计时器列表遍历,逐个减1。这就是”循环套循环”:外层while控制时间流逝,内层for处理每个计时器。
  • 先记账再删:遍历列表的时候不能直接删除元素——边遍历边删会漏掉元素(Python的经典陷阱)。所以先用到点的列表把”该删的”记下来,遍历完了再统一删。这个小心思,值得记住。
  • 列表.remove(元素):按内容删除。跟del 列表[下标](按位置删除,第十二篇用过)是一对兄弟。
  • " | ".join(...):把所有计时器的状态拼成一行,煮面0:12 | 烤箱18:03。第二十篇刚用过join。

现在煮面、烤箱、炖汤三个计时器一起跑,谁到点提醒谁,其余继续倒计时。

九、第三个改进:可视化进度条

等待的时候干着急?加个进度条,让等待”看得见”:

def 进度条(剩余秒数, 总秒数, 名称):
    """画一个文字进度条"""
    完成比例 = 1 - 剩余秒数 / 总秒数      # 0.0到1.0之间的小数
    宽度 = 20                            # 进度条总格数
    已满 = int(完成比例 * 宽度)           # 已经填了几格
    条 = "█" * 已满 + "░" * (宽度 - 已满)   # 满的+空的
    print(f"r{名称} [{条}] {完成比例 * 100:5.1f}%", end="")

效果:

煮面 [████████░░░░░░░░░░░░]  40.0%

原理全是老知识:

  • 剩余秒数 / 总秒数:剩余比例;1 - 它 = 完成比例
  • "█" * 已满:字符串乘以数字 = 重复。第四篇数据类型的小技巧:"哈" * 3 → "哈哈哈"
  • 完成比例 * 100:5.1f:百分比保留1位小数,占5个字符宽(这样数字跳动时不会左右晃)

要在多个计时器下用,就把显示状态改成对每个计时器调用进度条,并且记住每个计时器的初始总秒数(在字典里加一个”总秒数”字段,添加时记下来)。

十、回顾一下用到了什么

知识点 在哪里用的

|——–|———–|

time.sleep() 暂停1秒、响铃间隔
import time 导入时间模块
while循环 倒计时主循环
for循环 遍历计时器、响铃5次
条件判断 到点检查、命令判断
列表 计时器列表、到点记录
列表.append/remove 添加/删除计时器
字典 每个计时器的信息
整除//和取余% 秒数转”分:秒”
字符串乘法 进度条的█和░
try/except 时长输入验证
f-string格式化 :02d补零、:.1f小数位
r和end=”” 原地刷新显示
join 拼接多计时器简报
exit() 输入错误直接退出
函数 响亮提醒、添加计时器、进度条

十一、一个真实的坑:sleep不精确

用这个提醒器煮面时你可能发现:说好5分钟,它可能5分01秒才喊你。

原因:time.sleep(1)只是”至少睡1秒”。除了这1秒,程序执行print、判断、减法也要花一点点时间(几毫秒)。每次循环多花几毫秒,几百圈下来就慢了几秒。

煮面不差这几秒。但如果你在做科学实验级别的计时,正确做法是用”绝对时间”:记下开始时间time.time(),每次循环用”现在时间 – 开始时间”算已过秒数,而不是靠累加。这样执行再慢,时间也是准的。

今日小练习里就有这个。

十二、今日小练习

练习1:精确计时

把基础版改成”绝对时间”计时:开始时记下开始时刻 = time.time(),循环里用已过 = time.time() - 开始时刻算已过秒数,剩余 = 总秒数 - 已过。对比一下和累加版的差异。

练习2:时分秒输入

现在只支持”几分钟”。改成支持”1小时20分钟”这种输入:让用户分别输入小时和分钟(小时可跳过),换算成总秒数。再想想怎么支持”45秒”的超短计时。

练习3:输入命令解析

多计时器版里,添加计时器和开始倒计时的交互有点笨。重构成:用户输入add 煮面 5就添加一个5分钟的”煮面”计时器(用split()拆字符串,第二十篇练过),输入go开始,输入list看状态。三种命令分发,用if/elif/else。


下一篇:简易记账本 — 每天记一笔花了多少钱,月底看看钱都去哪了。文件存数据、列表记账目、函数做统计——第十二篇价格工具的”理财升级版”。

评论

发表回复

您的邮箱地址不会被公开。 必填项已用 * 标注