AI编程(二十三):番茄钟计时器 — 把专注力管起来

作者:

在

学习25分钟、休息5分钟——把第二十一篇的倒计时升级成完整的工作法工具

一、为什么你学不进去,可能不是意志力的问题

你有没有过这种经历:坐下来学习,刚看了两页书,手机亮了;回完消息想继续,脑子里开始想晚饭吃什么;等回过神,半小时没了,书还在第2页。

结论通常是”我意志力太差”。但有个意大利人不同意。

1980年代,Francesco Cirillo发明了一种方法:不要求你学一整天,只要求你专注25分钟。25分钟后休息5分钟,算一个”番茄”。攒够4个番茄,来一次长休息。

就这么简单,但它风靡全世界。原理很反直觉:把”学习一晚上”这种吓人的目标,拆成”熬过25分钟”这种做得到的小任务,大脑就不抗拒了。而且”番茄计数”会让你有种攒金币的快感。

今天我们用程序实现它。这一篇会用到:time模块(第二十一篇刚学的)、datetime(记录日期时间)、循环(工作→休息→工作的循环套循环)、条件判断(长短休息的切换)、文件操作(记录完成统计)、格式化输出(进度显示)。

做完这个,你就有了一个能天天用的专注力工具,还有它生成的每日报告。

二、番茄钟的工作流程

开始
 │
 ├─ 专注25分钟 🔥
 │    └─ 完成 → 番茄数+1
 ├─ 休息5分钟 ☕
 │
 ├─ 番茄数 < 4?→ 回去继续专注
 └─ 番茄数 = 4 → 长休息15分钟 → 番茄数清零,重新开始

注意这个结构:外层是”一轮一轮番茄”的循环,内层是”每轮里专注+休息”的流程。循环套循环——第六篇学的东西,今天要正经用一次。

三、先跟AI聊

你的提示词:

用Python写一个番茄钟计时器(命令行版)。规则:专注25分钟,完成后休息5分钟;每完成4个番茄钟,休息15分钟;长休息后番茄计数清零。用time.sleep每秒递减显示剩余时间(格式mm:ss),专注和休息阶段用不同文字提示。用循环和条件判断实现,中文界面,详细注释。

四、基础版代码

import time

# ========== 番茄钟设置 ==========
专注时长 = 25 * 60      # 25分钟,换算成秒
短休息 = 5 * 60          # 5分钟
长休息 = 15 * 60         # 15分钟
每轮番茄数 = 4           # 攒够4个来长休息

def 倒计时(秒数, 标签):
    """显示一个带标签的倒计时,数完为止"""
    剩余 = 秒数
    while 剩余 > 0:
        print(f"r{标签} {剩余 // 60:02d}:{剩余 % 60:02d}", end="")
        time.sleep(1)
        剩余 -= 1
    print()                # 换行,免得接上下一行

def 主循环():
    """番茄钟的主流程"""
    print("=== 🍅 番茄钟 ===")
    print("规则:专注25分钟 → 休息5分钟,攒够4个长休息15分钟")
    print("按 Ctrl+C 可以随时退出n")

    番茄计数 = 0          # 本轮完成了几个番茄

    while True:           # 番茄一轮一轮永远转下去
        # ---- 阶段1:专注 ----
        input("准备好开始专注了吗?按回车开始……")
        倒计时(专注时长, "🔥 专注中")
        番茄计数 += 1
        print(f"🎉 专注完成!这是第{番茄计数}个番茄!n")

        # ---- 阶段2:休息 ----
        if 番茄计数 % 每轮番茄数 == 0:
            # 攒够4个:长休息,然后清零
            print("🏆 一轮4个番茄全部完成!")
            倒计时(长休息, "🌴 长休息")
            番茄计数 = 0
        else:
            倒计时(短休息, "☕ 短休息")

        print("休息结束,回来继续!n")

主循环()

五、逐行理解

5.1 常量区:设置放在一起

专注时长 = 25 * 60
短休息 = 5 * 60
长休息 = 15 * 60
每轮番茄数 = 4

时长设置全部放在程序开头,用有意义的名字命名。想改成”专注50分钟”?改第一个数字就行,不用翻代码找魔法数字。把”可配置的东西”和”逻辑”分开,是写代码的好习惯。

5.2 倒计时函数:第二十一篇的老朋友

def 倒计时(秒数, 标签):
    剩余 = 秒数
    while 剩余 > 0:
        print(f"r{标签} {剩余 // 60:02d}:{剩余 % 60:02d}", end="")
        time.sleep(1)
        剩余 -= 1
    print()

和上一篇的倒计时一模一样,只是加了个标签参数——专注时显示”🔥 专注中”,休息时显示”☕ 短休息”。同一个函数,传不同参数,干不同的活。第七篇说的函数复用,这就是实例。

最后那个光秃秃的print()是换行:倒计时用了r原地刷新,数完后光标还停在那行上,补个换行让后面的输出从新行开始。小细节,但没有它输出会挤成一团。

5.3 取模判断:什么时候长休息

if 番茄计数 % 每轮番茄数 == 0:

%是取余数(第二十一篇学过)。番茄计数是4的倍数时(4、8、12……),除以4余数为0,该长休息了。

用取模判断”每N次做一次特殊的事”,是循环里的经典技巧:每逢3的倍数打印Fizz、每100条数据存一次盘、每4个番茄长休息……全是这一个套路。

5.4 双层循环

while True:                  # 外层:番茄无限循环
    倒计时(专注时长, ...)     # 内层:倒计时自己的循环
    ...
    倒计时(短休息, ...)       # 又一个内层循环

外层循环转一圈 = 一个完整的”专注+休息”。内层的倒计时循环则负责把这25分钟一秒一秒地走完。时间在两层循环里被”切”成了规整的小块——这正是番茄钟的精髓。

5.5 input当”开始按钮”

input("准备好开始专注了吗?按回车开始……")

这招很妙:用input暂停程序,等用户按回车才开始。它既是”确认”(人回来了吗),又是”仪式感”(按下按钮,进入状态)。没有它,程序一启动就倒计时,你去倒杯水回来已经过去3分钟了。

六、运行试试

python 番茄钟.py
=== 🍅 番茄钟 ===
准备好开始专注了吗?按回车开始……
🔥 专注中 24:59

然后把窗口放在屏幕角落,开始干活。25分钟后:

🎉 专注完成!这是第1个番茄!
☕ 短休息 04:59

用一天你会发现三个可以改进的地方:

  1. 每次专注都是25分钟,但我有时候只想专注15分钟啊
  2. 今天到底完成了几个番茄?程序没记住
  3. 想看看这一周我每天专注了多少

七、第一个改进:自定义时长

让玩家自己选专注时长。顺便学一个新写法——用字典当菜单:

# 预设方案:字典做菜单
时长方案 = {
    "1": ("标准番茄", 25, 5),
    "2": ("短冲刺", 15, 3),
    "3": ("深度工作", 50, 10),
    "4": ("自定义", None, None)     # None表示要手动输入
}

def 选择时长():
    """让用户选择专注时长,返回(专注分钟, 休息分钟)"""
    print("n选择专注方案:")
    for 编号, (名称, 专注分, 休息分) in 时长方案.items():
        if 专注分:
            print(f"  {编号}. {名称}(专注{专注分}分钟,休息{休息分}分钟)")
        else:
            print(f"  {编号}. {名称}")

    选择 = input("请选择:").strip()

    if 选择 not in 时长方案:
        print("无效选择,用标准番茄")
        return 25, 5

    名称, 专注分, 休息分 = 时长方案[选择]

    # 自定义:让用户自己输
    if 专注分 is None:
        try:
            专注分 = int(input("专注多少分钟(5-120):"))
            休息分 = int(input("休息多少分钟(1-30):"))
        except ValueError:
            print("输入不对,用标准时长")
            return 25, 5
        # 范围检查
        if not (5 <= 专注分 <= 120 and 1 <= 休息分 <= 30):
            print("超出范围,用标准时长")
            return 25, 5

    print(f"✅ {名称}:专注{专注分}分钟,休息{休息分}分钟")
    return 专注分, 休息分

主循环开头调用它:

专注分, 休息分 = 选择时长()
专注时长 = 专注分 * 60
短休息 = 休息分 * 60

几个亮点:

  • 字典当菜单:编号→(名称, 专注, 休息)。比一长串if-elif清爽,加方案只需加一行数据
  • is None判断:自定义方案的值是None,看到None就走手动输入分支。None是第四篇学过的”空值”,专门用来标记”这里还没东西”
  • 多层防御:选错菜单→默认标准;输非数字→默认标准;超范围→默认标准。每一步都有兜底,程序永远能给出一个可用的时长,绝不卡死
  • try...except包两个输入:一句except接住两个input的错,谁出错都走兜底

八、第二个改进:完成统计 — 程序记住你的战绩

现在程序重启就忘了今天攒了几个番茄。用第九篇的文件操作解决——每次完成番茄,就往文件里记一笔:

import json
from datetime import datetime

记录文件 = "番茄记录.json"

def 加载记录():
    """读取历史记录,返回 {日期: 番茄数} 的字典"""
    try:
        with open(记录文件, "r", encoding="utf-8") as f:
            return json.load(f)
    except FileNotFoundError:
        return {}          # 没有记录文件就从空开始
    except json.JSONDecodeError:
        print("⚠️ 记录文件损坏,重新开始")
        return {}

def 记录一个番茄(记录):
    """完成一个番茄,记录到今天名下"""
    今天 = datetime.now().strftime("%Y-%m-%d")
    记录[今天] = 记录.get(今天, 0) + 1     # 今天的数量+1

    # 立刻存盘,程序崩了记录也不丢
    with open(记录文件, "w", encoding="utf-8") as f:
        json.dump(记录, f, ensure_ascii=False, indent=2)

    return 记录[今天]

在主循环里,专注完成之后调用:

    番茄计数 += 1
    今日总数 = 记录一个番茄(记录)
    print(f"🎉 专注完成!第{番茄计数}个番茄(今天累计{今日总数}个)")

数据结构就是一个字典:{"2025-01-15": 5, "2025-01-16": 3, ...}——日期做键,当天番茄数做值。第二十二篇”按天分组累加”的同款结构,这次连分组都省了:天然按天分组,因为键就是日期。

注意记录[今天] = 记录.get(今天, 0) + 1——又是那个”取不到当0″的老套路。今天的第一个番茄:get返回0,加1存1。第二个:get返回1,加1存2。

每次完成立刻存盘,不等退出。这样程序中途崩溃(或者你直接关窗口),已完成的番茄也都在。重要的数据,尽早落盘——这是真实软件的常规做法。

九、第三个改进:日报表 — 看看这周的自己

有了记录文件,就能做报表了。加一个”查看统计”功能:

def 查看统计():
    """显示最近7天的番茄统计"""
    记录 = 加载记录()
    if not 记录:
        print("📭 还没有记录,先完成一个番茄吧!")
        return

    print("n📊 最近7天专注报告")
    print("=" * 38)

    # 生成最近7天的日期列表
    from datetime import timedelta
    今天 = datetime.now()
    总番茄 = 0
    总时长 = 0

    for 往前几天 in range(6, -1, -1):       # 6天前、5天前……今天
        日期 = (今天 - timedelta(days=往前几天)).strftime("%Y-%m-%d")
        数量 = 记录.get(日期, 0)
        总番茄 += 数量
        总时长 += 数量 * 25                 # 按标准番茄算时长

        # 用方块画个迷你柱状图
        条 = "🍅" * 数量 if 数量 else "—"
        星期名 = ["一", "二", "三", "四", "五", "六", "日"][
            (今天 - timedelta(days=往前几天)).weekday()]
        print(f"  周{星期名} {日期}  {数量}个 {条}")

    print("=" * 38)
    print(f"  7天总计:{总番茄}个番茄,约{总时长 // 60}小时{总时长 % 60}分钟")
    if 总番茄:
        print(f"  日均:{总番茄 / 7:.1f}个")

新知识点:timedelta日期运算

from datetime import timedelta
日期 = (今天 - timedelta(days=往前几天)).strftime("%Y-%m-%d")
  • timedelta(days=3):表示”3天”这个时间段
  • 日期 – timedelta:日期减去3天,得到3天前的日期

datetime模块能直接做日期加减,不用你自己算”这个月有没有31号”。第七篇说import能省很多事,这就是例子——日期计算这种麻烦事,标准库早就帮你做好了。

新知识点:weekday()

(今天 - timedelta(days=往前几天)).weekday()

weekday()返回星期几的数字:周一=0,周二=1……周日=6。用它当列表下标(星期名[数字]),就把数字变成”一二三四五六日”。数字→下标→文字,这个转换链条第四篇学过(数据类型+列表)。

画个迷你图表

条 = "🍅" * 数量 if 数量 else "—"

字符串乘法(第二十一篇进度条用过):几个番茄就画几个🍅。if 数量 else "—"是三元表达式——”有条件的选择”的紧凑写法。数量是0就画个横杠(0乘任何字符串是空字符串,光秃秃不好看)。

输出效果:

📊 最近7天专注报告
======================================
  周一 2025-01-13  3个 🍅🍅🍅
  周二 2025-01-14  5个 🍅🍅🍅🍅🍅
  周三 2025-01-15  0个 —
  周四 2025-01-16  4个 🍅🍅🍅🍅
  周五 2025-01-17  2个 🍅🍅
  周六 2025-01-18  0个 —
  周日 2025-01-19  1个 🍅
======================================
  7天总计:15个番茄,约6小时15分钟
  日均:2.1个

一周的专注情况一目了然。数据被看见,才会被改变——这就是报告的意义。

十、几个值得注意的设计决定

回头看整个程序,有几个决定值得琢磨:

1. 记录用字典而不是列表。 如果用列表存[{"日期":..., "数量":...}],查”今天有几个”得遍历整个列表。用日期做键的字典,记录.get(今天, 0)一步到位。数据结构跟着查询方式选——总是按键查,就用字典。

2. 完成即存盘。 不是退出时才保存,而是每完成一个番茄立刻写文件。多花几毫秒,换来崩溃不丢数据。

3. 倒计时做成函数,专注和休息共用。 加个标签参数就区分了场景。如果专注和休息各写一遍倒计时,代码直接多一倍。

4. 每层输入都有兜底。 选错菜单、输错数字、超出范围,全部有默认值接住。用户怎么折腾,程序都能给出可用的时长。

十一、回顾一下用到了什么

知识点 在哪里用的

|——–|———–|

time.sleep 每秒倒计时
while循环 番茄主循环、倒计时循环
循环套循环 主流程套倒计时
for循环 遍历7天、遍历菜单
range(6, -1, -1) 倒序生成6→0
条件判断 长短休息切换、输入验证
取模% 每4个番茄长休息
函数 倒计时、选择时长、记录、报表
函数参数 标签、秒数
return 返回时长、返回统计
字典 时长方案菜单、按天记录
字典.get 累加番茄数、查某天数量
None 标记自定义方案
is None 判断走不走自定义分支
列表+下标 数字转星期名
文件操作 JSON记录读写
try/except 多种错误兜底
datetime/timedelta 日期、日期减N天
weekday() 星期几
字符串乘法 🍅柱状图
三元表达式 数量为0画横杠
f-string格式化 mm:ss显示、对齐

十二、今日小练习

练习1:暂停功能

专注到一半来电话了怎么办?现在的程序没法暂停。加一个功能:倒计时期间用户按回车可以暂停,再按回车继续。(提示:这是个难题——time.sleep期间程序听不见键盘。可以每秒醒来时用某种方式检查键盘。先问问AI有什么办法,你会学到”非阻塞输入”这个概念。做不出来也没关系,思考过程本身就是收获。)

练习2:导出周报

写一个函数,把本周(周一到周日)的记录导出成一个文本文件”周报.txt”,内容包括每天的番茄数、总计、日均、最好的一天是哪天。提示:"w"模式写文件,max()函数找最大值(配字典的.get用:max(记录, key=记录.get))。

练习3:任务标签

让每个番茄可以贴标签:开始专注前输入”这25分钟做什么”(如:写论文、刷数学题),记录改成{"2025-01-15": {"写论文": 2, "刷数学题": 3}}——字典套字典。统计时能看到”这周写论文花了几个番茄”。想想哪些函数要跟着改?


下一篇(阶段收官):个人档案管理器 — 把这一路学的所有东西装进一个程序:证件信息、账号密码、联系方式,增删改查、搜索统计、加密存储、导出报表。这是阶段2的毕业作品。

评论

发表回复

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