Python 环境加载中…

自学一个新模块

4 道练习题·预计 60 分钟·做对一题解锁下一段

完整通读本章 →

教学 01 / 10· 已读

前言

各位童鞋,这是基础部分的最后一个实战章了。

前面几章,每个新东西都是教程一步一步讲给你听的。可等你真正自己写程序的时候,会发现要用的模块成百上千,不可能每一个都有人手把手地教。

所以这一章换个学法:咱们自己学会一个没讲过的模块。

学哪个呢?就学 Python 自带的 datetime 。它在第 9 章只是露了个面,演示了怎么创建一个日期。这一章,咱们要用它给记账本的每一笔账加上日期,再算出最近 7 天花了多少钱。

那又该怎么学呢?不靠背,靠这几样:先说清楚要做什么,再去官方文档里找,然后用 dir() 和 help() 看看有哪些工具,最后用几个小例子验证自己的理解。这套办法,换成任何一个模块都管用。

自学的时候,看不懂文档,或者试出来的结果和想的不一样,都是常事。碰到这种情况,别急着去搜现成的答案,先自己多试几次。因为自己试出来的东西,记得最牢。

学这一章之前,需要先学完 实战 5 的记账本 v4,以及 第 9 章 的「学会用一个陌生模块」和「几个常用的标准库」。

目录

这一章会学到:

  • 一、账单缺了日期——光知道花了多少,不知道是哪天花的
  • 二、自学一个模块,分四步——先列需求,再找文档
  • 三、今天是几号?——date 、date.today() 和 fromisoformat()
  • 四、7 天前是哪天?——用 timedelta 算日期
  • 五、显示成「11月02日」——strftime() 和格式代码
  • 六、常见坑
  • 七、自己查一次文档——一周从星期几算起?
  • 八、记账本 v5
  • 九、小结与回看

学完这一章,你的记账本会给每一笔账记上日期,还能算出最近 7 天花了多少钱;更重要的是,你会有一套自学新模块的办法。

教学 02 / 10· 已读

一、账单缺了日期

上一章,记账本 v4 已经能把账存进文件了。可是打开 my_ledger.json 看一看,每一笔账只有三样东西:

文本
{
  "item": "奶茶",
  "category": "餐饮",
  "cost": 12.5
}

买了什么、什么类别、花了多少,唯独没有哪天花的。

少了日期,会有什么麻烦呢?假如账记了一个月,想知道这个星期花了多少、哪天花得最多,就没法算了。

所以这一章要给每一笔账加上日期。具体来说,要做到这几件事:

  1. 记账的时候,自动记下今天的日期。
  2. 日期能存进 json 文件,下次还能读回来。
  3. 算出「7 天前」是哪一天。
  4. 判断某一笔账是不是在最近 7 天里。
  5. 显示的时候,写成「11月02日」这样好看的样子。

这些事,Python 自带的 datetime 模块都能做。可它到底怎么用,前面没讲过。

这回,咱们自己学。怎么学呢?下一节先说说自学一个模块的办法。

教学 03 / 10· 已读

二、自学一个模块,分四步

其实自学一个模块的办法,第 9 章「学会用一个陌生模块」里已经用 math.ceil 演示过一回:先明确需求,再去文档里找,然后用正数、零和负数验证。这一章把它展开成四步:

  1. 列需求:用大白话写清楚要做什么。上一节已经列好了。
  2. 找文档:去官方文档找到这个模块的那一页,先看目录,知道它大概有哪些东西。
  3. 小步试:用 dir() 看看有哪些方法,用 help() 看说明,每个方法写两三行代码试一试。
  4. 用边界验证:拿特殊的值去试,比如月底、年底、闰年,看结果是不是和你想的一样。

第一步上一节已经做完了,接下来做第二步:找文档。datetime 模块的官方中文文档在这里:datetime --- 基本日期和时间类型 。

这一页很长,别想着从头读到尾。先看开头的介绍和目录,能找到这几个名字:

  • date :日期,年、月、日。
  • time :一天里的时间,时、分、秒。
  • datetime :日期加时间。
  • timedelta :两个日期或时间之间的差,比如「7 天」。

四个名字里,该先看哪个呢?记账只关心哪一天,不关心几点几分,所以先盯住 date 。这就到了第三步,先用 dir() 看看它都有些什么:

Python· 可在网页运行
from datetime import date

print([name for name in dir(date) if not name.startswith('_')])

输出的结果:

文本
['ctime', 'day', 'fromisocalendar', 'fromisoformat', 'fromordinal', 'fromtimestamp', 'isocalendar', 'isoformat', 'isoweekday', 'max', 'min', 'month', 'replace', 'resolution', 'strftime', 'timetuple', 'today', 'toordinal', 'weekday', 'year']

代码里那一行列表生成式,是干什么的呢?因为 dir() 列出来的名字里,有一大堆以下划线开头的,那是 Python 内部用的。所以这里用第 7 章的列表生成式,把它们筛掉了。

筛完还剩二十个名字。要全学吗?不用。对着上一节列的需求,挑出看着有关系的就行:

需求看着有关系的名字
拿到今天的日期today
存进文件、再读回来isoformat 、fromisoformat
年、月、日year 、month 、day
星期几weekday 、isoweekday
显示成好看的样子strftime

那「7 天前」这个需求呢?date 里没有看着像的名字。不过别忘了,文档目录里还有 timedelta ,名字里的 delta 就是「差值」的意思,应该就是它了。

清单列好了,接下来就照着它,一个一个地试。

教学 04 / 10· 已读

三、今天是几号?

清单上的第一个需求,是拿到今天的日期,所以先试 today 。看看说明书怎么说:

Python· 可在网页运行
from datetime import date

help(date.today)

说明书的主要内容是这样的:

文本
today(...) class method of datetime.date
    Current date or datetime:  same as self.__class__.fromtimestamp(time.time()).

说明书里的 Current date ,就是当前的日期。文档里写得更直白:返回当前的本地日期。到底是不是这样,试一下就知道了:

Python· 可在网页运行
from datetime import date

print(date.today())
print(type(date.today()))

输出的结果(第一行是你运行那天的日期):

文本
2026-11-02
<class 'datetime.date'>

再看第二行:拿到的不是字符串,而是一个 date 对象。

不过有个问题:date.today() 每天运行的结果都不一样,拿来做例子不方便。所以下面的例子,都用 date(年, 月, 日) 创建一个固定的日期。这种写法,第 9 章见过:

Python· 可在网页运行
from datetime import date

d = date(2026, 11, 2)
print(d)
print(d.year, d.month, d.day)

输出的结果:

文本
2026-11-02
2026 11 2

从输出可以看到,用 d.year 、d.month 、d.day ,就能分别拿到年、月、日。

日期拿到了,接下来看清单上的「存进文件、再读回来」。实战 5 说过,json 只认数字、字符串、列表、字典这些东西。那日期该怎么存呢?可以先把日期变成字符串存进去,读回来的时候,再变回日期。

清单里的 isoformat() 和 fromisoformat() ,名字刚好是一对,看着就像是干这个的。先看一眼 fromisoformat() 的说明:

Python· 可在网页运行
from datetime import date

help(date.fromisoformat)

说明书的主要内容是这样的:

文本
fromisoformat(...) class method of datetime.date
    str -> Construct a date from a string in ISO 8601 format.

开头的 str -> ,意思是给它一个字符串,它造出一个日期。那要什么样的字符串呢?后面写着:ISO 8601 格式。ISO 8601 是一种国际通用的日期写法,就是 2026-11-02 这样的「年-月-日」。

说明看懂了,就动手试一试,看日期能不能变成字符串,再变回来:

Python· 可在网页运行
from datetime import date

d = date(2026, 11, 2)
text = d.isoformat()
print(text, type(text))

back = date.fromisoformat(text)
print(back, type(back))
print(back == d)

输出的结果:

文本
2026-11-02 <class 'str'>
2026-11-02 <class 'datetime.date'>
True

对照三行输出来看:

  • 第一行:isoformat() 把日期变成了字符串 '2026-11-02' 。
  • 第二行:fromisoformat() 再把它变回了日期。
  • 第三行:True 说明,变回来的日期和原来的一模一样。

这样一来,存文件的问题就解决了。清单上接下来是「7 天前是哪一天」,下一节接着试。

教学 05 / 10

四、7 天前是哪天?

第二节猜过,「7 天前」这个需求要靠 timedelta 。文档里它的样子是 timedelta(days=0, seconds=0, ...) ,参数一长串,咱们只用得到 days 。

猜一下:日期减去 timedelta(days=7) ,是不是就是 7 天前?试试:

Python· 可在网页运行
from datetime import date, timedelta

d = date(2026, 11, 2)
print(d - timedelta(days=7))
print(d + timedelta(days=30))

输出的结果:

文本
2026-10-26
2026-12-02

猜对了。而且加 30 天的时候,它自己知道 11 月只有 30 天,算到了 12 月 2 日,不用咱们操心每个月有几天。

不过,只试一两个普通的日期,还不够放心。这就到了四步里的第四步:用边界验证。日期最容易出错的就是月底、年底和闰年,挑几个试试:

Python· 可在网页运行
from datetime import date, timedelta

print(date(2026, 3, 1) - timedelta(days=1))
print(date(2028, 3, 1) - timedelta(days=1))
print(date(2026, 12, 31) + timedelta(days=1))

输出的结果:

文本
2026-02-28
2028-02-29
2027-01-01

一行一行看:2026 年不是闰年,3 月 1 日的前一天是 2 月 28 日。2028 年是闰年,前一天就是 2 月 29 日。年底再加一天,自动跨到了下一年。三个都对。

日期减去几天,得到的还是日期。那反过来,两个日期相减,得到的又是什么呢?

Python· 可在网页运行
from datetime import date

gap = date(2026, 12, 25) - date(2026, 11, 2)
print(gap)
print(type(gap))
print(gap.days)

输出的结果:

文本
53 days, 0:00:00
<class 'datetime.timedelta'>
53

看第二行就知道,两个日期相减,得到的也是一个 timedelta 。要拿到相差几天,用它的 .days ,也就是第三行的 53 。

再看清单上的下一个需求:判断某一笔账是不是在最近 7 天里。这要用到日期的另一个本事:日期之间还能直接比大小,越晚的越大:

Python· 可在网页运行
from datetime import date, timedelta

today = date(2026, 11, 2)
week_ago = today - timedelta(days=7)
print(date(2026, 10, 30) >= week_ago)
print(date(2026, 10, 20) >= week_ago)

输出的结果:

文本
True
False

代码先算出 7 天前是哪天,再拿两个日期和它比。10 月 30 日在最近 7 天里,10 月 20 日不在。这样一来,「判断某一笔账是不是在最近 7 天里」,这个需求也解决了。

日期的加减和比较都试过了,先做一道练习:读入两个日期,算出它们相差几天。

练习 1 / 4两个日期相差几天题目有问题?

读入两个 2026-11-02 这样格式的日期,打印它们相差几天。

「程序输入」框里预填了 2026-11-02 和 2026-12-25,运行后完整的输出应该是:

文本
开始日期:2026-11-02
结束日期:2026-12-25
相差 53 天

检查答案时还会用跨过 2 月底的日期,以及两个相同的日期。

main.py
可编辑

「运行」使用上面的输入;「检查答案」会依次使用题目给定的 3 组输入。

做对当前题解锁下一段 ·04

推荐一个,全网最全最友好的中文 AI 编程指南: coding.stormzhang.ai

学习目录