鸣众数研A Voice for Every Researcher
基础 / Python 零基础编程 / 单元综合练习

Python 单元综合练习:用 20 条合同公告制作金额表

用 20 条政府采购合同公告练习金额换算,保留同名合同,算出一张可以按公告出处逐笔核对的金额表。

课前准备安装软件、下载数据与运行方法

本课用 Python 运行代码,在 JupyterLab 新建和保存文件。建议先学变量、循环、函数和文件读写,再做综合练习。第一次使用时,点击必备软件下载,按说明安装 Python 和 JupyterLab。

  1. 建项目并放入数据。在文件资源管理器中新建 lesson01,再在里面建 data。如果你从桌面的 JupyterLab 图标打开软件,就把项目建在它打开的工作文件夹里。下载本课数据与参数,全部解压,把 contracts.csv(合同公告)、practice.csv(模拟练习)和 settings.json(筛选门槛)直接放进 data。数据表的列名是中文。
  2. 新建代码文件。在 JupyterLab 左侧进入 lesson01,点左上角 +,选 Other → Python File。右键左侧新出现的文件,选 Rename,改名为 amount_analysis.py。它应该和 data 文件夹并排。
  3. 复制、粘贴并修改项目路径。点课程代码里的“复制完整代码”,点回 JupyterLab 中 amount_analysis.py 的编辑区,按 Ctrl + V。在文件资源管理器中打开自己的 lesson01 文件夹,点地址栏,按 Ctrl + C。把第 1 段中标红的示例路径 D:\research\lesson01 换成刚复制的完整地址,保留两侧的引号。路径填到 lesson01 这一层。按 Ctrl + S 保存脚本。
  4. 运行整份脚本。在代码编辑区点右键,选 Create Console for Editor,如提示选择,选 Python 3 (ipykernel) 并确认。等控制台准备好,点回 amount_analysis.py 的编辑区,选 Run → Run All Code。控制台会显示读入数据的位置、各项计算结果,以及汇总表的完整路径。
  5. 想逐段观察时。保留刚连接的同一个 Python 控制台,在编辑区选中从 # 第1段 到第 2 段标题之前的完整代码,按 Shift + Enter 或选 Run → Run Selected Code。看完控制台的输出,再选中并运行第 2 段,依次做到第 6 段。后面会用到前面定义的变量和函数。

需要复习创建和运行脚本的操作?看第一课的 JupyterLab 实拍示例。

想用现成示例?下载完整课堂包,全部解压后打开 先读我-如何运行课堂包.txt。

回到课程代码 ↓

本课能学到什么用 Python 读取合同数据,把万元换算成元,再用记录数和总额核对导出的金额表。

课程代码

查看课前准备

第 1 段 · 找到项目和输入文件

amount_analysis.py · 第 1–16 行
1# 第1段 找到项目和输入文件2from pathlib import Path3from decimal import Decimal, InvalidOperation4import csv5import json6 7# 把引号里的示例换成你电脑上 lesson01 文件夹的完整路径。8项目目录 = Path(r"D:\research\lesson01")9数据目录 = 项目目录 / "data"10结果目录 = 项目目录 / "output"11for 文件名 in ("contracts.csv", "practice.csv", "settings.json"):12    输入文件 = 数据目录 / 文件名13    if not 输入文件.is_file():14        raise FileNotFoundError(f"找不到输入文件:{输入文件}。请核对项目目录,并把三份文件解压到项目中的 data 文件夹。")15print("读取数据的位置:", 数据目录)16 
03 / 核对成果

打开自己生成的金额表

运行结束后,控制台会显示成果表的完整路径。在文件资源管理器打开 lesson01/output/amount_summary.csv,找到“跟做:公告样本”这一行。默认数据应有 20 条,合计 12,405,922.20 元。想知道总额里有哪些合同,打开同目录的 contract_amounts.csv,从合同编号和公告链接查起。

查看详细参考表(默认参数)

跟做和改做使用 2026 年 11 月 4 日留存的 20 条政府采购合同公告。独立作业使用 7 条自建模拟记录。本表供对照,不代表全国统计,也不会随你的本地运行刷新。

任务记录数合计元口径
跟做:公告样本2012405922.20全部金额已统一为元
跟做:同名印刷合同22780.10按不同合同编号保留
改做:不低于10000元1312375809.63以元比较;包含等于阈值
独立做:有效记录415350.00自建模拟样本;包括已确认的零
独立做:待核查3缺失、非数字和未知单位不计入有效合计

下次学习,从 JupyterLab 左侧双击保存好的 amount_analysis.py。改了数据或筛选参数,保存后再运行,程序会更新 output 中的同名文件。

04 / 继续学习

讲义与作业

课程讲义 · 10 节

01 把学习任务缩到一张可以核对的表

本课从一张已有的公告表开始,把金额从万元换成元,再按条件筛选。先不要记住所有Python语法。打开contracts.csv,观察合同编号、合同名称、合同金额(万元)和公告详情链接四列。每一行是一条合同公告记录;不同合同可以有同样的名称,不能只按名称删重。脚本将保留合同编号和出处,让转换后的金额还能回到原记录。

这20条是2026年11月4日留存的一次政府采购合同公告列表样本。我们沿用已整理材料,不在运行时访问网站,也不声称本轮重新采集。首页记录会变动,样本规模和来源选择都限制了外推。12405922.20元是这20条记录的课堂合计,不能写成全国采购额或某行业的全年规模。

先给自己设一个可检查的终点:生成五张成果CSV,解释首条金额为什么是9992.47元,保留两份不同编号的印刷合同,再在新样本里区分缺失和零值。网页能核对你填入的数字,但文件是否保留来源、处理规则是否合理仍需要按人工标准检查。

02 创建、保存并运行自己的 .py 文件

在 JupyterLab 左侧进入“lesson01”,点左上角 +,在启动页选 Other → Python File 创建文件,把它重命名为“amount_analysis.py”。在编辑区粘贴网页代码,改好项目目录,按 Ctrl + S 保存。文件名要以 .py 结尾。

点击代码编辑区,右键选择 Create Console for Editor 并选择 Python,等控制台启动完毕,再点回代码编辑区,从 Run 菜单选择 Run All Code。控制台依次显示读取数据的位置、公告合计、模拟记录检查和成果表位置。

03 变量、字符串与数字:先判断它代表什么

代码中的“金额文本”是一个变量名,里面保存从CSV读来的内容。CSV读取器默认给我们字符串,所以"0.999247"是一段文字。两个字符串用加号会拼接,如"10"+"20"得到"1020";要做数值计算须先转换。合同编号即使全是数字,也通常应当保留字符串,例如独立作业的001不应变成1,否则可能与另一张表匹配失败。

等号表示赋值,把右边的结果存到左边;双等号判断是否相等。先手写一个小实验:金额文本 = "0.999247",再输出print(金额文本),应看到原文字。字符串外面的引号不会作为内容打印。中文或英文变量名都可以使用,但同一个名字要前后一致;“总额”和“金额合计”是两个不同变量,不会自动互相替代。

本课特意保留原金额列和单位说明。数据类型不是研究含义:字符串可以代表金额,也可以代表编号。更不能看到字段里有0就决定它是未采购、未观测还是缺失。类型转换之前先读原表含义,类型转换之后再核对观察单位和数值范围。

04 从万元到元:把单位写进函数

第一条公告为0.999247万元,手算0.999247乘以10000得到9992.47元。函数转成元把同一规则集中在一个位置:输入原金额文字和单位,返回以元计的Decimal。定义def只登记规则,不会自动处理整张表;只有调用转成元("0.999247", "万元")时才执行函数内部语句。return将计算结果交回调用处,print只是显示信息。

金额使用Decimal从字符串直接构造。许多十进制小数不能在二进制浮点中精确表示;对账时不宜先转成float,再转Decimal补救。本课不将“显示两位小数”等同于“原数据精确到分”:先乘倍率,再将结果与量化到0.01的值比较。如果多出了分以下的金额,就要求核查,而不是悄悄四舍五入掩盖原始精度问题。

函数只接受元和万元两种已明确的单位。独立样本中的美元会进入待核查表,因为货币兑换还需要明确汇率日期和来源;本课没有这些依据。空白金额、待确认文字、负值和非有限值也不能作为普通非负合同金额直接累计。确认含义为零的"0"则允许保留,这一差别会影响有效记录数。

05 相对路径与CSV:读到的是哪一个文件

第一段的项目路径指向 lesson01。在这个项目下面,data 只放输入,output 专门放生成的结果。需要搬动整个项目时,从文件资源管理器复制新位置,更新第 1 段中的路径即可。

with语句管理文件的打开和关闭;csv.DictReader把表头变成每行字典的键。例如行["合同金额(万元)"]读取这一列,列名里的中文括号也是名称的一部分。若把表头改为“金额”,旧代码不会猜测你的意思,而会报KeyError。打开文件时使用utf-8-sig兼容本课CSV的UTF-8标记,newline=""交给CSV模块处理换行。

list把读到的各行装进列表,len可以检查行数,列表的首个位置是0。本课应读到20行数据,表头不计入数据行。先打印第一条合同编号,再看第一条金额,不要只看程序有没有结束。读取错误的同名旧文件时,程序也可能顺利完成,输入名称、数量与首行核对能帮助发现这种情况。

06 用循环和条件逐笔计算,不按合同名称删重

for 行 in 公告依次取出每条记录。冒号后缩进的代码属于循环体,会重复执行;缩进结束后才进入汇总阶段。每轮先取合同编号,再换算本条金额。编号集合使用set记录已经出现过的编号;编号为空或重复时停止参考样本处理,要求回查。集合适合做存在性判断,金额表列表则保留需要导出的每一行。

总额是把已统一为元的金额逐笔相加,初始值为 Decimal("0")。本程序用 sum 累加金额表中的每笔金额。手算前两条,9992.47 元加 375.60 元等于 10368.07 元,再用 20 条总额检查是否算全。

if 行["合同名称"] == "印刷服务合同"只选择名称完全相同的记录,并不把它们合为同一笔交易。这里有两个不同合同编号,对应375.60元和2404.50元,合计2780.10元。按名称删掉其中一条会减少真实记录。是否重复需要更完整的业务键和来源核对,本课只检查这份小样本中合同编号的唯一性。

07 改做练习:把阈值变成可以检查的参数

打开settings.json,默认基准阈值元为100000、改做阈值元为10000。它们是以元计的数字字符串,代码读取后转为Decimal。条件>=表示“大于或等于”,因此恰好100000元的合同也进入10万元及以上组。不要把参数10000误写成1,再把标签仍写作1万元。参数和结果表说明必须使用同一种单位。

原样本中不低于10万元有11条,门槛降为1万元后有13条,筛后合计12375809.63元,新增的两笔为80000元和31700元。手算检查:新增金额为111700元,应该等于两种阈值下合计的差。数量增加而金额减少,通常提示累计对象、单位或筛选逻辑出了问题。

完成默认题后可把改做阈值改为50000,再运行并观察哪些记录离开集合。此时网页默认题目的13条答案不再适用,作业提交前应恢复10000。主程序在默认参数下执行精确答案断言,改参数时仍检查降低阈值不应减少记录的基本关系。研究中的筛选规则要有理由和版本记录,不能仅因结果好看就换阈值。

08 读报错与隔离异常:从最后一行找到具体对象

报错不等于数据应被删掉。先读最后一行的异常类型与说明,再往上看属于你脚本的文件名和行号。FileNotFoundError通常与路径或文件有关;KeyError通常与表头名称有关;NameError说明使用了尚未定义的名字。SyntaxError或IndentationError可能发生在真正计算之前,检查对应位置附近的冒号、引号、括号和缩进。

转成元遇到"待确认"会抛出ValueError。跟做的真实样本采用严格策略,发现异常应停下来核对;独立作业则演示如何逐条保留待核查记录,再继续处理其他可确认的行。try包住预期可能失败的转换,except只接收ValueError,else处理转换成功的记录。不要用不区分类型的except把所有程序错误吞掉,再一律写零。

practice.csv是原创的7条教学记录。001为1.20万元,002为850元,005为已确认0元,006为0.25万元;其余三条分别为空白、待确认和美元。正常结果应有4条有效记录、3条待核查,有效合计15350元。这个总额只是已确认记录的合计,不包含待核查项的未知金额。

09 写出文件后再读回,保留能解释的成果

写文件时先写中文表头,再用writerows写入各行。不要用字符串拼接逗号来造CSV;合同名称本身可能含逗号或换行,CSV模块会处理必要的引用。本课导出contract_amounts.csv、filtered_contracts.csv、valid_records.csv、review_records.csv与amount_summary.csv。金额元列用两位小数字符串,编号和来源链接保持。

保存成功后再读contract_amounts.csv,检查20行和12405922.20元是否保持。屏幕中打印的总额正确,并不能证明写入文件用了同一批记录或同一个变量。若表格软件把001自动显示成1,先以文本编辑器或Python读回原CSV判断文件内容;单纯修改表格中的显示格式不一定能恢复已经丢失的原编号。

按 Ctrl + S 保存的是代码。运行程序后,“output”里会出现五份 CSV 和run_result.json。提交作业时同时说明输入表、金额单位和待核查规则;只给出一个总额不足以还原处理过程。

10 用同一个项目独立处理一份新表

在同一项目内新建“independent_analysis.py”,沿用已经改好的项目路径。读取“data”里的“practice.csv”,自行写出金额换算函数,把记录分成有效与待核查两组。

在“output”下用“student_valid.csv”和“student_review.csv”保存自己的输出,再与第 5 段的结果对照。检查 005 的零值是否保留,003、004 和 007 为何需要核查。

最后用一段话解释这份样本中的有效金额合计包括哪些记录、排除了哪些金额。如果换成其他业务的数据,还需要先确认观察单位、金额单位和编号含义。

阅读材料

下载原版配套讲义 PDF(按旧版课堂包编写,文件操作以本页为准。)

课后作业 · 跟做、改做与独立做

我的学习记录

学习记录只保存在当前浏览器。完成状态由章节勾选、数值核对和作业自查共同组成,不是能力认证。

进入分层作业

分层作业:跟做、改做、独立做

数值检查只覆盖本题指定口径。代码质量、解释与识别条件请逐项自查,不由网页自动判定。

跟做:完成20条公告金额换算

使用“data”里的contracts.csv,运行自己的amount_analysis.py。手算首两笔,再核对逐笔金额表里的合同编号和两份同名印刷合同。

提交内容:contract_amounts.csv,加一段说明观察单位、金额单位和同名记录为何保留。

提示 1

先确认表头原单位为万元;首条应换算成9992.47元。

提示 2

合同编号不同的两份印刷合同都应保留,不能按名称删重。

按每个输入框标注的单位填写数值,不加单位文字;可以填写小数。

查看参考解释

真实样本20条,合计12405922.20元。两份印刷合同375.60元与2404.50元合计2780.10元。

改做:将筛选门槛从10万元降到1万元

在“data”文件夹中打开settings.json,对照 100000 元与 10000 元两个门槛(包含等于)。保存修改后重新运行amount_analysis.py,比较记录数和总额。

提交内容:filtered_contracts.csv,加一张新增合同编号与金额的手工核对记录。

提示 1

>=包含等于阈值。先将输入金额都变成元再比较。

提示 2

新增两笔为80000元和31700元,可以核对集合差与总额差。

按每个输入框标注的单位填写数值,不加单位文字;可以填写小数。

查看参考解释

10万元门槛11条,1万元门槛13条;新门槛合计12375809.63元,新增2条合计111700元。

独立做:处理一份混合单位与缺失的模拟表

在同一项目新建independent_analysis.py,读取“data”里的practice.csv。只接受元和万元,把空白、非数字与未知单位保留待核查,保留确认的零。

提交内容:output 文件夹中的student_valid.csv、student_review.csv,以及自己写的independent_analysis.py,附三种待核查原因。

提示 1

先把换算写成函数;单位检查、空白检查、Decimal转换分别处理。

提示 2

001=12000元,002=850元,005=0元,006=2500元;这些是核对线索,不能直接硬编码成总额。

按每个输入框标注的单位填写数值,不加单位文字;可以填写小数。

查看参考解释

原创模拟7条;有效4条、待核查3条、有效合计15350元。网页数值检查不能自动判断代码是否硬编码,也不能替代异常处理说明。

遇到问题怎样定位

运行时提示找不到输入文件

原因:项目路径写成了示例地址,或三份输入多嵌套了一层文件夹。

操作:从文件资源管理器复制“lesson01”的地址到第一段的“项目目录”。检查报错里“data”下的具体文件名。

复查:再次运行,控制台应显示正确的数据目录。

Run 菜单没有 Run All Code,或不知道到哪里看输出

原因:当前选中的是文件列表、数据表或控制台。

操作:在左侧双击amount_analysis.py,在右侧代码文字上右键选 Create Console for Editor,确认 Python。回到代码编辑区,再打开 Run 菜单。

复查:运行完整文件后,控制台会打印读取数据的位置和成果表的位置。

KeyError: 合同金额(万元)

原因:代码要求的表头与CSV第一行不同,或读取了另一份文件。

操作:用文本编辑器查看第一行,核对中文括号与列名;恢复本课输入,不凭猜测替换字段含义。

复查:转成元获得字符串数值,首笔为9992.47元。

NameError或IndentationError

原因:只执行中间几行、变量拼写不一致,或循环/函数的缩进被破坏。

操作:确认六段已经按顺序粘贴在同一个 .py 文件,再运行整个文件。定位报错行,检查变量名、冒号和缩进。

复查:再次从文件开头运行,程序应写出成果表。

金额文字不能转换,或待核查记录突然变少

原因:存在空白、待确认或未知单位;也可能被错误地替换成0。

操作:保留原始文字和编号,先核对原因。独立作业将预期的ValueError写入待核查表,其他异常继续报出。

复查:有效4条、待核查3条;合法0元的005仍在有效表。

保存了代码,但在项目里找不到结果表

原因:Ctrl + S 保存的是代码,或者程序中途遇到了错误。

操作:回到代码编辑区选择 Run → Run All Code。先看控制台最后一行,按报错修正后再次运行。

复查:控制台最后给出“成果表位置”,打开这个完整路径对应的amount_summary.csv。

改阈值后网页数值检查不通过

原因:网页题目的口径固定为10000元,而本地参数可能已经改为别的值。

操作:先记录新阈值实验的结果,再把settings.json恢复默认100000和10000,重新运行。

复查:默认改做结果13条、12375809.63元。

补充练习与参考答案

1. 使用“data”里的contracts.csv,运行自己的amount_analysis.py。手算首两笔,再核对逐笔金额表里的合同编号和两份同名印刷合同。

真实样本20条,合计12405922.20元。两份印刷合同375.60元与2404.50元合计2780.10元。

2. 在“data”文件夹中打开settings.json,对照 100000 元与 10000 元两个门槛(包含等于)。保存修改后重新运行amount_analysis.py,比较记录数和总额。

10万元门槛11条,1万元门槛13条;新门槛合计12375809.63元,新增2条合计111700元。

3. 在同一项目新建independent_analysis.py,读取“data”里的practice.csv。只接受元和万元,把空白、非数字与未知单位保留待核查,保留确认的零。

原创模拟7条;有效4条、待核查3条、有效合计15350元。网页数值检查不能自动判断代码是否硬编码,也不能替代异常处理说明。

课堂自测 独立模拟样本中,编号005是0元,003为空白。怎样处理才符合本课规则?


← 返回完整课程目录

返回三门样板课学习路线 →