﻿Python入门：从第一段代码到可核对的金额表

跟做和改做使用2026-11-04已留存的20条政府采购合同公告，仅限这次列表窗口；每条保留公告详情链接。独立做为原创7条模拟数据。所有运行离线，不重新采集，也不代表全国统计。

把学习任务缩到一张可以核对的表
本课从一张已有的公告表开始，把金额从万元换成元，再按条件筛选。先不要记住所有Python语法。打开公告金额练习.csv，观察合同编号、合同名称、合同金额（万元）和公告详情链接四列。每一行是一条合同公告记录；不同合同可以有同样的名称，不能只按名称删重。脚本将保留合同编号和出处，让转换后的金额还能回到原记录。
这20条是2026年11月4日留存的一次政府采购合同公告列表样本。我们沿用已整理材料，不在运行时访问网站，也不声称本轮重新采集。首页记录会变动，样本规模和来源选择都限制了外推。12405922.20元是这20条记录的课堂合计，不能写成全国采购额或某行业的全年规模。
先给自己设一个可检查的终点：生成五张成果CSV，解释首条金额为什么是9992.47元，保留两份不同编号的印刷合同，再在新样本里区分缺失和零值。网页能核对你填入的数字，但文件是否保留来源、处理规则是否合理仍需要按人工标准检查。

在 JupyterLab 运行第一段 Python 代码
完成软件准备后，完整解压 Windows 课堂包。双击“打开本课.cmd”，浏览器会打开“课堂学习.ipynb”。点进第一格代码，按 Shift + Enter，查看下方打印的课程目录。接着点下一格代码，按顺序完成换算、筛选与写表。
左侧文件列表中的 CSV 是课程数据。课堂学习.ipynb 用于逐格操作，课堂练习.py 保留同一计算过程的原脚本。按 Ctrl + S 保存笔记本；最后的写表代码会在解压目录另存课堂成果。重新核对时可以重启内核并从第一格顺序运行。

变量、字符串与数字：先判断它代表什么
代码中的“金额文本”是一个变量名，里面保存从CSV读来的内容。CSV读取器默认给我们字符串，所以"0.999247"是一段文字。两个字符串用加号会拼接，如"10"+"20"得到"1020"；要做数值计算须先转换。合同编号即使全是数字，也通常应当保留字符串，例如独立作业的001不应变成1，否则可能与另一张表匹配失败。
等号表示赋值，把右边的结果存到左边；双等号判断是否相等。先手写一个小实验：金额文本 = "0.999247"，再输出print(金额文本)，应看到原文字。字符串外面的引号不会作为内容打印。中文或英文变量名都可以使用，但同一个名字要前后一致；“总额”和“金额合计”是两个不同变量，不会自动互相替代。
本课特意保留原金额列和单位说明。数据类型不是研究含义：字符串可以代表金额，也可以代表编号。更不能看到字段里有0就决定它是未采购、未观测还是缺失。类型转换之前先读原表含义，类型转换之后再核对观察单位和数值范围。

从万元到元：把单位写进函数
第一条公告为0.999247万元，手算0.999247乘以10000得到9992.47元。函数转成元把同一规则集中在一个位置：输入原金额文字和单位，返回以元计的Decimal。定义def只登记规则，不会自动处理整张表；只有调用转成元("0.999247", "万元")时才执行函数内部语句。return将计算结果交回调用处，print只是显示信息。
金额使用Decimal从字符串直接构造。许多十进制小数不能在二进制浮点中精确表示；对账时不宜先转成float，再转Decimal补救。本课不将“显示两位小数”等同于“原数据精确到分”：先乘倍率，再将结果与量化到0.01的值比较。如果多出了分以下的金额，就要求核查，而不是悄悄四舍五入掩盖原始精度问题。
函数只接受元和万元两种已明确的单位。独立样本中的美元会进入待核查表，因为货币兑换还需要明确汇率日期和来源；本课没有这些依据。空白金额、待确认文字、负值和非有限值也不能作为普通非负合同金额直接累计。确认含义为零的"0"则允许保留，这一差别会影响有效记录数。

相对路径与CSV：读到的是哪一个文件
笔记本用 Path.cwd() 取得当前课程文件夹；原脚本用 Path(__file__).resolve().parent 取得脚本所在文件夹。目录 / "公告金额练习.csv"在这个位置寻找输入，因此从别的工作目录启动也能找到同级文件。它不会在整台电脑中自动搜索，也不能从只复制过来的脚本旁边变出数据。文件找不到时，先核对解压是否完整、文件名和扩展名是否一致，以及脚本旁是否有输入CSV。
with语句管理文件的打开和关闭；csv.DictReader把表头变成每行字典的键。例如行["合同金额（万元）"]读取这一列，列名里的中文括号也是名称的一部分。若把表头改为“金额”，旧代码不会猜测你的意思，而会报KeyError。打开文件时使用utf-8-sig兼容本课CSV的UTF-8标记，newline=""交给CSV模块处理换行。
list把读到的各行装进列表，len可以检查行数，列表的首个位置是0。本课应读到20行数据，表头不计入数据行。先打印第一条合同编号，再看第一条金额，不要只看程序有没有结束。读取错误的同名旧文件时，程序也可能顺利完成，输入名称、数量与首行核对能帮助发现这种情况。

用循环和条件逐笔计算，不按合同名称删重
for 行 in 公告依次取出每条记录。冒号后缩进的代码属于循环体，会重复执行；缩进结束后才进入汇总阶段。每轮先取合同编号，再换算本条金额。编号集合使用set记录已经出现过的编号；编号为空或重复时停止参考样本处理，要求回查。集合适合做存在性判断，金额表列表则保留需要导出的每一行。
总额从Decimal("0")开始累加。把总额初始化放进循环体，会导致每轮清零，最终往往只留下最后一笔金额。原脚本用sum对已统一单位的金额列表求和，作用同样是逐项累计。新手可以把前两条手算：9992.47元加375.60元等于10368.07元；再用20条总额检查循环是否覆盖全部记录。
if 行["合同名称"] == "印刷服务合同"只选择名称完全相同的记录，并不把它们合为同一笔交易。这里有两个不同合同编号，对应375.60元和2404.50元，合计2780.10元。按名称删掉其中一条会减少真实记录。是否重复需要更完整的业务键和来源核对，本课只检查这份小样本中合同编号的唯一性。

改做练习：把阈值变成可以检查的参数
打开筛选参数.json，默认基准阈值元为100000、改做阈值元为10000。它们是以元计的数字字符串，代码读取后转为Decimal。条件>=表示“大于或等于”，因此恰好100000元的合同也进入10万元及以上组。不要把参数10000误写成1，再把标签仍写作1万元。参数和结果表说明必须使用同一种单位。
原样本中不低于10万元有11条，门槛降为1万元后有13条，筛后合计12375809.63元，新增的两笔为80000元和31700元。手算检查：新增金额为111700元，应该等于两种阈值下合计的差。数量增加而金额减少，通常提示累计对象、单位或筛选逻辑出了问题。
完成默认题后可把改做阈值改为50000，再运行并观察哪些记录离开集合。此时网页默认题目的13条答案不再适用，作业提交前应恢复10000。主程序在默认参数下执行精确答案断言，改参数时仍检查降低阈值不应减少记录的基本关系。研究中的筛选规则要有理由和版本记录，不能仅因结果好看就换阈值。

读报错与隔离异常：从最后一行找到具体对象
报错不等于数据应被删掉。先读最后一行的异常类型与说明，再往上看属于你脚本的文件名和行号。FileNotFoundError通常与路径或文件有关；KeyError通常与表头名称有关；NameError说明使用了尚未定义的名字。SyntaxError或IndentationError可能发生在真正计算之前，检查对应位置附近的冒号、引号、括号和缩进。
转成元遇到"待确认"会抛出ValueError。跟做的真实样本采用严格策略，发现异常应停下来核对；独立作业则演示如何逐条保留待核查记录，再继续处理其他可确认的行。try包住预期可能失败的转换，except只接收ValueError，else处理转换成功的记录。不要用不区分类型的except把所有程序错误吞掉，再一律写零。
独立练习_模拟金额.csv是原创的7条教学记录。001为1.20万元，002为850元，005为已确认0元，006为0.25万元；其余三条分别为空白、待确认和美元。正常结果应有4条有效记录、3条待核查，有效合计15350元。这个总额只是已确认记录的合计，不包含待核查项的未知金额。

写出文件后再读回，保留能解释的成果
写文件时先写中文表头，再用writerows写入各行。不要用字符串拼接逗号来造CSV；合同名称本身可能含逗号或换行，CSV模块会处理必要的引用。本课导出跟做_逐笔金额.csv、改做_阈值筛选.csv、独立做_有效记录.csv、独立做_待核查.csv与课堂成果.csv。金额元列用两位小数字符串，编号和来源链接保持。
保存成功后再读跟做_逐笔金额.csv，检查20行和12405922.20元是否保持。屏幕中打印的总额正确，并不能证明写入文件用了同一批记录或同一个变量。若表格软件把001自动显示成1，先以文本编辑器或Python读回原CSV判断文件内容；单纯修改表格中的显示格式不一定能恢复已经丢失的原编号。
每次运行参考入口会重建参考成果，不会改写输入CSV。自己的作业请使用学生有效记录.csv和学生待核查.csv，避免覆盖参考文件。交作业时同时写明使用哪个输入、以什么单位计算、哪些记录被保留待核查。仅交一个正确的总额，无法说明程序的处理过程是否可信。

从参考程序转到自己的作业
顺序运行“课堂学习.ipynb”得到基准结果后，在侧栏用编辑器打开“作业起步.py”，填写 TODO 并保存。回到笔记本，在末尾新增一个 Code 格，输入 %run 作业起步.py 后按 Shift + Enter。起步文件的 NotImplementedError 会指向等待你补全的函数。修改后再运行这个作业格即可。
起步文件已负责读入新CSV和遍历记录，留下金额函数、输出和汇总几个任务。按TODO顺序完成：先支持元/万元，再判断空白与无效数字，保留确认零，最后输出有效与待核查两张表。不能直接把15350写成总额，也不能删掉三条异常后声称没有问题。完成后用网页数值检查，再对照人工标准查看编号、单位和异常原因。
实在卡住时先看分层作业.txt中的提示，再看参考答案.txt。作业参考代码.py是一种完整实现，可以读来比较；如果要核对参考实现，在笔记本末尾的 Code 格输入 %run 作业参考代码.py 后运行。请先另存自己的输出文件，以免同名作业结果被覆盖。数值一致只是第一关，还要解释为什么本样本允许零值、未知单位为何等待核查，以及换到新业务时哪些规则必须重审。