前面这十章,我们其实一直在做一件事:
让你的 Python 代码,从“能跑的小练习”,一步步走向“更像正式项目的代码”。
你已经学了:
为什么代码要拆文件 什么是模块、什么是包import 的几种写法 自己写的代码也可以变成模块__name__ == '__main__' 到底在控制什么 Python 为什么自带那么多标准库os、sys、random、datetime、math、statistics、collections 这些工具各自解决什么问题
如果这些内容一直分开看,很多人会觉得:单个知识点我懂了,但真要我自己写一个稍微像样的小程序,还是不知道怎么把它们拼起来。
所以这一章,我们就不再拆着讲,而是做一个完整的综合实战。目标非常明确:
把模块、导入、主程序入口、标准库这些内容真正串起来,写一个更像“正式项目”的小工具。
这次我们做一个非常适合当前阶段的项目:
命令行随机点名与记录工具
这个项目不大,但很有代表性。因为它会同时用到:
模块拆分 自定义模块 标准库 程序入口 路径处理 时间记录 随机功能
而这些,正好就是这一阶段最该落地的内容。
一、先看需求,不要急着写代码
还是老规矩。 一上来不要先敲代码,先把需求拆清楚。
我们现在想做一个小工具,功能如下:
读取一个学生名单 随机抽取一名同学 显示抽中的结果 记录抽取时间 把抽取结果保存到本地文件 程序可以反复运行 代码不要全堆在一个文件里
你会发现,这个需求虽然简单,但已经非常像真实的小工具了。
它不是只在终端里打一行结果就完事。 它还涉及:
输入数据 随机逻辑 时间处理 文件输出 模块组织
这就特别适合用来练“更像项目的写法”。
二、先想结构:哪些代码应该放在一起
如果你现在已经有模块意识,就应该先想到一件事:
这些内容不应该全部堆进 main.py。
更合理的拆法可以是这样:
picker.py负责随机点名逻辑
file_utils.py负责文件读写相关逻辑
main.py负责程序入口和整体流程
如果你想再规范一点,还可以加一个数据文件,比如:
students.txt
于是整个小项目可以长成这样:
roll_call_project/ students.txt picker.py file_utils.py main.py
你看,这就是模块思维真正落地的样子。
不是说“我知道模块是什么”, 而是“我知道哪些代码应该放在哪个文件里”。
这一步非常关键。
三、第一步,先准备学生名单文件
先建一个 students.txt,内容比如这样:
张三李四王五赵六小明小红
你现在可能会觉得,这不就是一份普通文本吗。
没错。 但一旦你开始做小工具,就会发现:很多程序的第一步,本来就是从一份本地数据开始。
所以这个项目的输入,就是这份名单文件。
四、第二步,写 file_utils.py,专门处理文件相关能力
既然我们要读名单、还要写抽取记录,那文件操作就应该集中管理。
可以写成这样:
# file_utils.pydefload_students(filename): students = []with open(filename, 'r', encoding='utf-8') as f:for line in f: name = line.strip()if name: students.append(name)return studentsdefsave_result(filename, content):with open(filename, 'a', encoding='utf-8') as f: f.write(content + '\n')
这一段代码其实很有代表性。
load_students()负责读取名单文件,并返回一个列表
save_result()负责把抽取结果追加保存到文件
这里你会发现,前面学的文件读写、with、函数封装,其实都已经自然用上了。
而且很重要的一点是:
文件逻辑被单独放进了一个模块。
这意味着主程序不用再自己管怎么读文件、怎么写文件。 主程序只管调用它。
这就是模块带来的清晰感。
五、第三步,写 picker.py,专门处理随机抽取逻辑
随机点名显然和 random 模块有关,所以可以把这部分单独放到另一个模块里:
# picker.pyimport randomdefpick_student(students):ifnot students:returnNonereturn random.choice(students)
这段代码非常短,但很重要。
为什么?
因为它在做一件特别“模块化”的事:
把随机抽取这件事,封装成一个明确的小功能。
以后只要你有一个学生列表,谁来抽,都用这个函数。 而不是每次都在主程序里自己写一遍:
random.choice(students)
这就是“把能力整理成模块函数”的味道。
六、第四步,主程序 main.py 负责把一切串起来
现在轮到主程序出场了。
它要做的事情其实很清楚:
读取学生名单 随机抽一个人 拿到当前时间 把结果打印出来 再保存到记录文件
于是可以写:
# main.pyfrom datetime import datetimefrom file_utils import load_students, save_resultfrom picker import pick_studentdefmain(): students = load_students('students.txt')ifnot students: print('名单为空,无法抽取')return winner = pick_student(students) now = datetime.now().strftime('%Y-%m-%d %H:%M:%S') result = f'抽中学生:{winner},时间:{now}' print(result) save_result('result.txt', result) print('结果已保存到 result.txt')if __name__ == '__main__': main()
这一段代码特别值得你认真看一遍。
为什么它像“正式项目”的代码?
因为它没有把所有逻辑都糊在一个文件里。 它是通过模块之间的分工,把整个功能拼起来的。
load_students()来自文件工具模块
pick_student()来自随机点名模块
datetime.now()来自标准库
main()负责主流程
if __name__ == '__main__':负责主程序入口
这已经非常像一个小型正式脚本了。
七、这段代码里,模块和标准库是怎么配合的
这一章的核心,其实就在这里。
你会发现,程序并不是只靠自定义模块,也不是只靠标准库,而是两者配合。
自定义模块负责:
你的业务逻辑 你的项目结构 你的功能拆分
标准库负责:
随机能力 时间能力 文件系统基础支持
比如这里:
picker.py 是你自己写的模块file_utils.py 也是你自己写的模块datetime 是标准库random 也是标准库
这就很像真实开发了。
你自己搭项目骨架,标准库负责补上高频通用能力。
这句话你可以记住。它特别能概括这一阶段的本质。
八、为什么这个项目比“全写一个文件”强很多
我们来直接对比一下。
如果你把所有东西都堆进一个文件,会怎么样?
读名单的代码和随机逻辑混在一起 保存结果的代码和时间处理混在一起 主流程和底层工具细节混在一起 文件一长,自己看着都累
而现在这个拆法有什么好处?
file_utils.py 一看就知道是处理文件的picker.py 一看就知道是抽取逻辑main.py 一看就知道是程序入口
这带来的提升,不只是“看起来更整洁”,而是:
更容易维护 更容易定位问题 更容易替换功能 更容易复用代码
比如以后你想把随机点名改成随机抽 3 人,只要改 picker.py 相关逻辑就行。 主程序几乎不用大动。
这就是模块化真正的价值。
九、为什么 main() 和 __name__ == '__main__' 组合特别重要
你现在应该已经能看出,这个项目里:
defmain(): ...
和:
if __name__ == '__main__': main()
是一组非常自然的搭配。
为什么这样写更好?
因为主流程被单独封装成了一个函数。 文件被导入时,不会自动乱跑。 只有你直接运行 main.py,程序才真正开始执行。
这会让结构特别清楚:
上面是定义 下面是入口
这一点在小项目里尤其重要。 因为代码一旦开始拆模块,你就不再适合所有文件一打开就立刻执行一堆逻辑。
十、再把 os 也接进来,让项目更稳一点
前面这个版本已经能用了。 但如果我们想让它更像正式脚本,还可以继续加一点 os 模块的味道。
比如先检查名单文件是否存在:
# main.pyimport osfrom datetime import datetimefrom file_utils import load_students, save_resultfrom picker import pick_studentdefmain():ifnot os.path.exists('students.txt'): print('students.txt 文件不存在,程序结束')return students = load_students('students.txt')ifnot students: print('名单为空,无法抽取')return winner = pick_student(students) now = datetime.now().strftime('%Y-%m-%d %H:%M:%S') result = f'抽中学生:{winner},时间:{now}' print(result) save_result('result.txt', result) print('结果已保存到 result.txt')if __name__ == '__main__': main()
这样一来,程序就更稳了。
如果名单文件压根不存在,它不会直接崩。 而是先检查,再给出清楚提示。
这就是标准库在真实项目里特别实用的地方:
不是花哨,而是让脚本更稳。
十一、如果再把 sys 也接进来,程序还能更灵活
比如你不想把名单文件名写死成 students.txt,而是想从命令行传进来。
那就可以写成这样:
# main.pyimport osimport sysfrom datetime import datetimefrom file_utils import load_students, save_resultfrom picker import pick_studentdefmain():if len(sys.argv) < 2: print('用法:python main.py 名单文件名')return filename = sys.argv[1]ifnot os.path.exists(filename): print(f'{filename} 文件不存在,程序结束')return students = load_students(filename)ifnot students: print('名单为空,无法抽取')return winner = pick_student(students) now = datetime.now().strftime('%Y-%m-%d %H:%M:%S') result = f'抽中学生:{winner},时间:{now}' print(result) save_result('result.txt', result) print('结果已保存到 result.txt')if __name__ == '__main__': main()
这样运行时可以这样写:
python main.py students.txt
你看,项目味道是不是一下更浓了。
现在它已经不只是一个“写死的小脚本”,而是开始具备:
可配置输入 环境检查 主流程入口 模块化组织
这其实已经是非常标准的小工具写法了。
十二、这时候你应该真正理解“更像正式项目的代码”是什么意思了
很多人一开始会把“正式项目代码”想得特别复杂,仿佛一定要上数据库、上网络、上前后端才算正式。其实不是。
所谓“更像正式项目的代码”,往往先体现在这些地方:
有清晰的模块拆分 有主程序入口 有异常或边界检查 有标准库辅助能力 有输入输出的明确边界 有一定的复用意识
而这一章这个小项目,已经把这些感觉都带出来了。
它当然还不大。 但它已经明显不是那种“为了练语法临时拼一下”的代码了。
这一步特别重要。因为它会让你逐渐摆脱一种误区:
不是项目一定要做得很大才算项目。 而是结构开始像项目,思路开始像项目,那就已经在往项目代码靠近了。
十三、这个小项目里,每个文件各自负责什么
这一步你最好能清楚说出来。
file_utils.py负责和文件读写有关的底层能力
picker.py负责随机抽取逻辑
main.py负责程序整体流程控制
标准库 random负责随机能力
标准库 datetime负责时间记录
标准库 os负责文件存在性检查
标准库 sys负责获取命令行参数
你会发现,这种拆法特别像真实工作中的分工:
谁干自己的事 谁负责主流程 谁负责底层工具 谁负责外部输入
只要你能把这种“职责分配感”看清,模块和标准库这几章就是真的学进去了。
十四、再做一个小升级:把结果按日期分文件保存
如果你想让这个工具更像一点正式产品,可以继续优化。
比如结果文件不固定写成 result.txt,而是每天一个文件。 这时候 datetime 和 os.path.join() 就可以继续上场。
例如:
import osfrom datetime import datetimetoday = datetime.now().strftime('%Y-%m-%d')filename = os.path.join('records', f'{today}.txt')
你甚至可以再配合目录检查:
ifnot os.path.exists('records'): os.mkdir('records')
这样结果就可以按日期归档。
你会发现,一旦模块和标准库都熟一点,项目扩展会非常自然。 不是重写,而是在现有骨架上继续长功能。
这就是好结构带来的优势。
十五、这章最应该学到的,不是这几个文件,而是“组合能力”
说到底,这一章真正想训练你的,不是背 random.choice() 还是背 datetime.now()。
而是让你开始具备一种能力:
把多个知识点自然组合起来,完成一个完整的小工具。
你前面学的东西,其实一直都不是为了分开摆着的。
模块 导入 自定义模块__name__ == '__main__'标准库 文件读写 路径判断 命令行参数 随机 时间
这些东西一旦组合起来,才真正开始形成项目能力。
而“会组合”,恰恰就是很多人从练习阶段走向实战阶段的分水岭。
十六、一个更完整、适合自己敲一遍的版本
下面我把这个小项目整理成一套更完整、适合你自己跟着敲的版本。
file_utils.py
defload_students(filename): students = []with open(filename, 'r', encoding='utf-8') as f:for line in f: name = line.strip()if name: students.append(name)return studentsdefsave_result(filename, content):with open(filename, 'a', encoding='utf-8') as f: f.write(content + '\n')
picker.py
import randomdefpick_student(students):ifnot students:returnNonereturn random.choice(students)
main.py
import osimport sysfrom datetime import datetimefrom file_utils import load_students, save_resultfrom picker import pick_studentdefmain():if len(sys.argv) < 2: print('用法:python main.py 名单文件名')return filename = sys.argv[1]ifnot os.path.exists(filename): print(f'{filename} 文件不存在,程序结束')return students = load_students(filename)ifnot students: print('名单为空,无法抽取')return winner = pick_student(students) now = datetime.now().strftime('%Y-%m-%d %H:%M:%S')ifnot os.path.exists('records'): os.mkdir('records') result_file = os.path.join('records', 'result.txt') result = f'抽中学生:{winner},时间:{now}' print(result) save_result(result_file, result) print(f'结果已保存到 {result_file}')if __name__ == '__main__': main()
这套代码已经非常值得你亲手敲一遍了。 因为它把这一整个阶段最核心的东西基本都串起来了。
十七、这章结束后,你应该具备的感觉
如果这一章你真的看懂了,那你应该开始有下面这种感觉:
我不是只能写一个文件的脚本了 我开始知道怎么拆模块了 我开始知道主程序入口该怎么写了 我开始知道标准库怎么自然接入项目了 我开始知道代码结构怎么才能更像一个小项目了
这其实已经是很大的进步了。
因为很多人卡了很久,并不是不会语法,而是不会把语法组织成一个完整的小工具。 而这一章,本质上就在帮你过这个坎。
十八、本章小练习
你可以基于这个随机点名工具,再自己做两个升级版练习。
第一个练习:
把“随机抽 1 人”改成“随机抽 3 人”。 这会逼着你思考 random 模块里是不是还有更适合的函数,以及结果怎么保存更合理。
第二个练习:
把结果文件改成按日期命名,比如:
records/2026-03-26.txt
这会逼着你继续练:
datetime.strftime()os.path.join()os.path.exists()os.mkdir()
只要你能把这两个小升级做出来,这一章的内容就算真的落地了。
十九、本章总结
这一章,我们没有再新增很多零碎知识点,而是把模块和标准库真正拼成了一个完整小项目。
你看到了怎么用模块拆分职责。 看到了怎么用自定义模块封装业务逻辑。 看到了怎么用 main() 和 __name__ == '__main__' 组织程序入口。 看到了怎么用 random、datetime、os、sys 这些标准库给项目补上真实能力。 也看到了“更像正式项目的代码”到底意味着什么:不是代码有多大,而是结构、边界、职责、复用意识开始变得清楚。
到这里,第九阶段就算正式收束了。 你已经不只是知道模块和标准库是什么,而是开始具备把它们用到真实小工具里的能力。
下一章,我们正式进入第十阶段:091|列表推导式:Pythonic 写法从这里开始。