本章精髓 循环让同一段代码作用于一千个对象,这是程序真正比人强的地方,也是它真正比人危险的地方。手工改名改错影响一两个文件,脚本改错影响全部文件,而且没有撤销键。所以本章的规矩是:先预演,后执行,留记录。
学完本章后,你应该能够:
- 指出一段代码里重复执行的是哪几行,并说明缩进决定了什么;
- 在批量操作之前跑一次预演,依据打印结果判断规则对不对;
- 为批量改名写清三件事:筛选条件、命名规则、重名时怎么办;
- 依据改名记录把文件名还原到执行之前的状态;
- 说清预演结果为什么可能与执行结果不一致,以及执行之后还要核对什么。
成品 · 三道防线 · 先要预演版
这是本课程里第一个会真正改动硬盘内容的程序。
1.1本章成品:批量重命名工具
成品是一个 Python 脚本。运行后,它把指定文件夹里的图片按统一规则重命名,其余类型的文件一律跳过;改名的同时写下一份新旧名称的对照记录,据此可以把名称还原。
这一次运行没有改动任何文件,箭头表示"将要改成"
第二行的 课堂照片_002-2.jpg 是重名处理的结果:目标名称已经被文件夹里原有的某个文件占用,所以序号后面追加了 -2。这类情况在批量改名里很常见,规则必须事先规定好,否则脚本要么中断,要么把别人覆盖掉。
1.2执行之前的三道防线
一条命令可以在一秒内改掉三百个文件的名称,同样也可以在一秒内把它们弄乱,而且没有撤销键。本章的所有操作都建立在三道防线之上。
抛开处理的是图片、文档还是表格,批量操作的结构都一样:
本章的代码就是这三步,其余部分都是围绕它们的保护措施
1.3本章新技巧:要求先出预演版
提出"批量重命名"这个需求,大模型默认给你的是一份可以直接改动文件的完整脚本。拿到手直接运行,第一次运行就是正式执行,检查的机会已经错过了。
所以本章的提问分两步:第一步只要预演版本,核对打印结果;第二步再要求补上执行部分。两步之间隔着一次人工核对,这次核对是整个流程里唯一不能省的环节。
1.4可直接复制的提示词
第一段里的六条规则,每一条都对应后面要讲的一个知识点:
1.5大模型的常见偏差
直接给出会实际改动文件的版本。 这是它的默认行为,因为需求本身就是"批量重命名"。要求里必须出现"先只给预演版本""不要对任何文件做实际改动"这两句,缺一句都可能拿到完整功能的脚本。
不处理重名冲突。 多个文件算出同一个名称时,脚本要么中断,要么覆盖。前者只是白跑一趟,后者会造成文件丢失,而且屏幕上不会有任何提示。冲突规则必须写进需求。
不写改名记录。 它默认改名是一次性动作,不会主动考虑还原。记录只能在执行过程中写入,一旦执行完成,原名称已经不存在,事后补写无从谈起。
把路径写成绝对路径。 它会自行假定一个路径写进代码,例如 C:\Users\User\Desktop\photos。这份脚本在你的机器上就跑不起来,更不用说发给同组同学。
第10章演示_批量改名.py 与 第10章演示_回退.py,两者的设置区结构与提示词得到的结果一致。完全没装 Python 的机器,可以先打开 第10章演示_改名沙盘.html,把设置区的每一项开关一次,看规则怎样改变结果。三个演示文件都不依赖网络。循环 · 排序 · 条件筛选 · 路径 · 补零
本章的核心内容集中在这一部分和下一部分。
这八个术语会原样出现在脚本参数、技术文档与运维规范里。前五个在本部分讲解,后三个在第三部分讲解。
2.1五个知识点,一个一个看
执行一次
代码只写一遍,执行次数由清单的长度决定
现象:代码里缩进的那几行只写了一遍,运行之后却对三十个文件都起了作用。
概念:循环(loop)指对一批对象重复执行同一段代码。for old in files: 的意思是:把 files 里的每一项依次取出,叫它 old,然后执行下面缩进的语句。缩进决定了哪些语句属于循环体,缩进错一格,执行次数就变了。
类比:流水线上每个零件都要经过同一道工序,工序只需要设置一次。
排序之后,序号的分配才是可预测的
现象:代码先用一行把文件列出来,之后才进入循环。
概念:批量处理的第一步是取得一份列表(list),也就是把待处理的对象一次性列出来。先取清单再处理,可以避免在遍历过程中因为文件被改名而漏掉或者重复处理。sorted 让处理顺序固定,同一批文件每次运行得到同样的结果。
类比:仓库发货前先打一份出货单,再照单取货,而不是一边找一边取。
大写的 .JPG 也要能被认出来,比较前统一转小写
现象:文件夹里的 .txt 文件在屏幕上显示"跳过",名称没有变化。
概念:条件筛选(if)用来只对满足条件的对象执行操作,本例的条件是扩展名属于 .jpg 或 .png。continue 表示跳过本次循环剩下的语句,直接处理下一项。筛选条件写得越明确,误改的可能性越小。
类比:分拣时只收带地址标签的包裹,广告传单一律不收。
写死盘符与用户名的路径,只在写它的那台机器上成立
现象:同一份脚本在同组同学的机器上提示找不到文件夹。
概念:文件路径(path)是文件在硬盘上的位置。绝对路径从盘符写起,里面带着用户名,换一台机器就不成立。本例改用脚本自身所在的位置作基准,文件夹跟着脚本一起走。路径里的空格与中文本身不会出错,出错的通常是把路径拆开拼接时漏了分隔符。
类比:写完整地址,和写"本楼三层",是两种定位方式。后者只在你确实站在这栋楼里时才成立。
两列都是按名称排序的真实结果
现象:改名之后按名称排列,第 10 张照片排到了第 2 张前面。
概念:文件名按字符逐位比较,第一位的 1 小于 2,所以 10 排在 2 前面。把序号补足固定位数叫补零(zero padding),补零之后名称排序与数字顺序一致。str(number).zfill(3) 表示不足三位时在左边补零。位数要按总量预留,超过一千个文件就该补足四位。
类比:门牌号写成 001、002,与写成 1、2,在按名称排列时会得到不同的顺序。
2.2本章的完整代码
下面这份代码由大模型生成。设置区的三项集中在最上方,其余部分对应上面的知识点。
from pathlib import Path
# ===== 设置区:只需要改下面三项 =====
FOLDER = '测试文件夹' # 待处理的文件夹,与本文件放在同一位置
PREFIX = '课堂照片' # 新文件名的前缀
DRY_RUN = True # True 为预演,只打印不改动;False 为真正执行
# ==================================
folder = Path(__file__).with_name(FOLDER)
# ① 先取清单:一次性列出文件夹里的文件并排序,再逐个处理
files = sorted(p for p in folder.iterdir() if p.is_file())
records = []
number = 0
# ② 循环:对清单里的每一个文件重复同样的处理
for old in files:
# ③ 条件筛选:只处理图片,其余文件跳过
if old.suffix.lower() not in ['.jpg', '.png']:
print('跳过', old.name)
continue
# ④ 组成新名称:序号补足三位,扩展名沿用原文件并转小写
number = number + 1
new = folder / (PREFIX + '_' + str(number).zfill(3) + old.suffix.lower())
# ⑤ 重名处理:目标名称已被占用时,在末尾追加 -2
if new.exists() and new != old:
new = folder / (new.stem + '-2' + new.suffix)
print(old.name, '→', new.name)
records.append(new.name + ' <- ' + old.name)
# ⑥ 预演状态下到此为止,不做任何实际改动
if not DRY_RUN:
old.rename(new)
# ⑦ 真正执行后写入改名记录,作为回退依据
if not DRY_RUN:
log = Path(__file__).with_name('改名记录.txt')
log.write_text('\n'.join(records), encoding='utf-8')
print('已处理', len(records), '个文件,改名记录已写入', log.name)
input('按回车键结束')
按本章方法核对:输入是设置区的三项加上文件夹里的清单,输出是屏幕上的每一行打印和最后写入的改名记录,重复执行的部分是 ② 之下缩进的全部语句,执行次数等于清单的长度。
下面这张表的第三列是反例:把这一行改掉或者删掉,会发生什么。
is_file() 让子文件夹不被计入。
不排序时,处理顺序由系统决定,同一批文件两次运行可能得到不同的序号dry run · 重名冲突 · 操作记录 · 预演的盲区
这一部分讲的全是保护措施,也是本章真正要带走的东西。
3.1另外三个知识点
改一项设置,同一份代码就在两种行为之间切换
现象:第一次运行只在屏幕上打印了"原名 → 新名",文件夹里什么都没变。
概念:预演(dry run)指先把全部判断与计算跑完,但不做任何实际改动,把将要发生的结果打印出来给人核对。批量操作一经执行就作用于全部对象,预演是执行之前唯一的检查环节。专业工具普遍提供这一功能,参数名通常就叫 dry-run。
类比:裁布之前先用粉笔画线,确认无误再下剪刀。
覆盖是不可见的,屏幕上不会有任何提示
现象:预演结果里出现了 课堂照片_002-2.jpg 这样带后缀的名称。
概念:目标名称已经被占用的情况叫重名冲突。Python 的 rename 在 Windows 上遇到重名会抛出 FileExistsError 并中断,在部分系统上则直接覆盖同名文件,被覆盖的那个文件随即消失。凡是自动生成名称的脚本,都必须事先规定冲突时怎么办,例如在末尾追加 -2、-3。
类比:两名同学重名时,点名册上要加学号区分,否则不知道叫的是谁。
每行一次改动,回退脚本按这份记录逐条还原
现象:执行之后,脚本旁边多出一个 改名记录.txt。
概念:操作记录(log)是新旧名称的对照表,每行记录一次改动。批量改名之后没法凭记忆还原,有记录才能回退(rollback)。记录必须在执行过程中写入,事后无法补写,因为那时原名称已经不存在了。
类比:调整仓库货位之前先拍一张原位置的照片,摆错了还能照原样放回。
3.2预演与执行的差别
两次运行用的是同一份代码,差别只有设置区的一项。第一次运行之后文件夹没有任何变化,第二次运行之后文件名全部更新,并且多出一个改名记录。
文件夹内容完全不变,也不生成改名记录。可以反复运行,直到打印结果符合预期。
执行结束后生成 改名记录.txt。这一步没有撤销键,只能靠记录回退。
文件夹在执行前后的对照如下。被跳过的四个文件保持原样,这一点在核对时最容易被忽略。
打开 第10章演示_改名沙盘.html,把设置区的每一项都开关一次:关掉补零看排序,关掉重名规则看覆盖,把预演开关拨到执行再点回退。这个沙盘不碰你硬盘上的任何文件,可以放心把每一种错误都犯一遍。
3.3预演的盲区
三道防线立好之后还剩一个漏洞,它是本章最值得记住的一句话。
打开 第10章演示_预演盲区.html,里面是一个三个文件的最小例子,一步一步走完就能看清这件事是怎么发生的。看完之后你会明白:预演给的是一份预测,不是一份保证。
核对的顺序是固定的:先看数量,再看名称。数量对不上说明有文件消失或者被合并,这是重伤,要立刻停手;数量对得上而名称不对,多半只是规则写错了,回退重来即可。
建环境 · 预演 · 执行 · 回退
从一个空白的练习环境开始,走完改名与还原的一个完整回合。
4.1演示步骤
4.2执行之后要核对什么
屏幕上的输出只能证明脚本自认为做了什么,证明不了硬盘上真的发生了什么。执行之后打开文件夹,按这个顺序看三样:
4.3前沿三分钟:可撤销与不可撤销的操作
每章固定栏目 · 一条与本章内容相关的行业动态
预演不是给初学者准备的辅助功能。文件同步工具 rsync、容器编排工具 kubectl、多数系统的软件包管理器,都提供 dry-run 参数,作用与本章设置区的那一项完全相同:把全部判断跑一遍,把将要发生的结果打印出来,不做任何改动。用这些工具的都是熟练的运维人员,他们在正式执行之前同样要先跑一次预演。
与之相关的还有另一条线索:回收站、撤销栈、版本控制系统,这些机制的出发点是同一个判断,即操作者一定会出错,系统应当保留改正的机会。判断一项功能设计得好不好,可以看它在出错之后留给使用者多少余地。
本章的三道防线是这套惯例的最小实现:测试文件夹对应隔离环境,预演对应 dry-run,改名记录对应撤销栈。三者都不复杂,缺任何一项都会在某一次操作里付出代价。
三选一 · 六步 · 预演与回退
上机环节由此开始,当堂完成并提交。
5.1三个题目,任选其一
把一批作业文件按"学号_姓名_作业名"的规则统一重命名。
通用读一批 txt 的内容,给含有指定关键词的文件在名称前统一加上标记。
偏文科读文件的修改时间,按年月建立子文件夹并把文件移进去。
偏理工三个题目都必须具备预演版本、重名处理规则、操作记录三项,验收标准一致。允许更换题材。练习一律在测试文件夹里进行,不得使用真实资料。
5.2上机六步
5.3共同验收标准
5.4常见错误与处理
encoding='utf-8',重新执行一遍5.5提交内容
提交方式:按教师提供的作业模板填写,文档开头注明所选题目。提交渠道见课堂通知,当堂提交。
5.6再进一步(选做)
选做一 命名规则依赖文件自身。把"前缀加序号"改成从文件自身读取,例如按修改时间命名为 20260315_001。改完之后会碰到一个新问题:同一天甚至同一分钟内修改的多个文件会算出相同的日期部分,冲突的数量比原来大得多。这时"追加 -2"就不够用了,要重新设计序号在名称里的位置。命名规则一旦依赖文件自身的属性,冲突处理规则就得跟着一起重新考虑。
选做二 让脚本会思考。把循环里的处理动作换成一次 AI 调用:读一批 txt 或者一张表的每一行,让模型给每条内容打一个标签或者写一句摘要,结果写回一个新文件。这一版的三道防线一条都不能少,而且要多加一条:先只处理前三条,看结果对不对,再放开处理全部。模型的输出不像改名那样可以逐字预测,所以抽查这一步在这里比预演更重要。
本章五条结论
- 循环让同一段代码作用于一批对象。缩进决定哪些语句属于循环体,条件筛选决定哪些对象被处理。
- 批量操作执行之后没有撤销键。先在测试文件夹里练习,先跑预演,核对通过之后再执行。
- 凡是自动生成名称的脚本,都要事先规定重名时怎么办。不作规定时,脚本或者中断,或者覆盖,而覆盖不会有任何提示。
- 操作记录是唯一可靠的回退依据,而且只能在执行过程中写入。
- 预演给的是预测,不是保证。执行之后仍要开文件夹核对,先看数量,再看名称。