- 示例工程
- 教程
【免费下载链接】python-mastery
Advanced Python Mastery (course by @dabeaz)
导读
本篇围绕 David Beazley 的 Advanced Python Mastery 课程 练习 1.3 展开,带你从零编写一个名为pcost.py的小程序:读取课程自带的 Data/portfolio.dat 数据文件,逐行解析每只股票的持股数量与买入单价,最终计算出整个投资组合的总成本。读完本文,你将掌握 Python 文件打开与读取、with上下文管理、逐行迭代、字符串split()切分以及int/float类型转换这一整套基础 I/O 编程套路,并能够把同样的模式迁移到其他文本格式的数据处理任务中。
练习定位:Python Review 阶段的第一道文件处理题
在课程结构中,练习 1.3 属于 "1. Python Review" 章节,是 练习索引 中排在基础语法复习(练习 1.1、1.2)之后的第一道涉及文件输入输出的题目。它的目标非常明确——回顾基本文件 I/O(Review basic file I/O)。课程假设学习者已经具备一定的 Python 基础(参见 README.md 的前置条件说明),因此这道练习用最小规模的代码,把文件操作中最核心的几个知识点串起来:
- 用
open()打开文件并读取内容; - 用
with语句确保文件被正确关闭; - 把文件对象当作可迭代对象,用
for line in f逐行处理; - 用
str.split()把一行文本切分成字段; - 用
int()、float()把字符串转换为数值并累加。
练习完成后生成的文件是pcost.py,该文件在后续练习中还会被反复改造(重构为函数、添加异常处理等),因此它实际上是整门课程早期的一条"主线代码"。
数据文件格式:理解 portfolio.dat 的列结构
练习使用的输入数据位于 Data/portfolio.dat,文件内容为 7 行空格分隔的文本:
AA 100 32.20 IBM 50 91.10 CAT 150 83.44 MSFT 200 51.23 GE 95 40.37 MSFT 50 65.10 IBM 100 70.44每一行代表一次股票买入记录,共三列:
| 列 | 含义 | 示例 |
|---|---|---|
| 第 1 列 | 股票代码(stock name) | AA、IBM |
| 第 2 列 | 持股数量(number of shares) | 100、50 |
| 第 3 列 | 每股买入价(purchase price of a single share) | 32.20、91.10 |
注意两个细节:同一只股票可能出现在多行(如MSFT和IBM各出现两次),因此不能按股票代码去重,而是要对每一行单独累加;第二列是整数(股数),第三列是浮点数(价格),解析时必须分别使用int()和float()转换。
计算总成本的方法在练习中写得很直白:求第二列乘以第三列的累加和,即total_cost = Σ (shares × price)。
需求拆解:一个"读文件 + 算总和"的最小流水线
练习 1.3 的任务可以拆成以下四步:
- 打开文件:读取
Data/portfolio.dat的全部内容; - 逐行读取:文件对象天然支持迭代,
for line in f每次取出一行(含末尾换行符\n); - 解析字段:用
line.split()按空白把一行切成字符串列表,例如'AA 100 32.20\n'→['AA', '100', '32.20']; - 转换并累加:
nshares = int(fields[1])、price = float(fields[2]),然后total_cost += nshares * price。
最终打印总成本即可。
逐步实现 pcost.py
下面按照课程练习要求,一步步写出pcost.py。
第一步:打开文件并逐行迭代
Python 打开文件的标准写法是open()配合with语句。with会在代码块结束时自动关闭文件,即使中途抛出异常也能保证资源释放,这是课程推荐并一直沿用的最佳实践:
with open('Data/portfolio.dat', 'r') as f: for line in f: print(line) # 先看看每一行的原始内容这里的第二个参数'r'表示只读模式(read),也是open()的默认模式,因此可以省略不写。文件对象f是一个可迭代对象,for line in f会按行产出内容,无需一次性把整个文件读入内存——这种逐行处理方式对大数据文件尤其友好。
第二步:解析每一行
对当前行调用split(),默认按任意连续的空白字符(空格、制表符等)切分,返回字段列表:
fields = line.split() # 'AA 100 32.20\n' -> ['AA', '100', '32.20']随后从列表中取出对应位置的数据并完成类型转换:
nshares = int(fields[1]) # 字符串 '100' -> 整数 100 price = float(fields[2]) # 字符串 '32.20' -> 浮点数 32.2第三步:累加并输出
把每一行的shares × price累加到total_cost变量中:
total_cost = 0.0 ... total_cost = total_cost + nshares * price完整代码
结合课程官方解答 soln1_3.md 与仓库中可直接运行的实现 Solutions/1_3/pcost.py,完整的pcost.py如下:
# pcost.py total_cost = 0.0 with open('Data/portfolio.dat', 'r') as f: for line in f: fields = line.split() nshares = int(fields[1]) price = float(fields[2]) total_cost = total_cost + nshares * price print(total_cost)注意:仓库内的 Solutions/1_3/pcost.py 由于位于Solutions/1_3/子目录,其文件路径写作'../../Data/portfolio.dat';而当你把pcost.py放在仓库根目录(课程 README.md 假设所有练习工作在顶层python-mastery目录中进行)时,应使用'Data/portfolio.dat'这一相对路径。
运行与验证
在仓库根目录执行:
bash % python3 pcost.py正确输出应为:
44671.15该数值与后续 练习 1.4 中交互式调用函数的验证结果完全一致(portfolio_cost('Data/portfolio.dat')返回44671.15),可以用于交叉校验你的实现是否正确。
如果希望手动核算,可以验证其中部分行:AA 100 × 32.20 = 3220.0,IBM 50 × 91.10 = 4555.0,全部 7 行累加之和即为44671.15。
关键知识点深挖
1. 为什么for line in f而不是f.read()后再 split?
文件对象本身是迭代器,for line in f按需逐行读取,内存占用恒定;而f.read()会把整个文件一次性读入内存,再手动split('\n')。对于本练习这种小文件两者皆可,但从源码习惯和后续大数据处理的角度,课程给出的逐行迭代写法是更通用、更省内存的模式,也是后面练习(如 练习 2.1 中的readrides.py、练习 8.x 的实时日志跟随)一直沿用for line in f的原因。
2. 换行符去哪了?
for line in f取出的每一行包含末尾的\n。在本练习中,line.split()会忽略换行符等空白字符,因此无需手动strip();但如果后续用fields[0]直接比较字符串或拼接输出,就要留意换行符的存在。练习 1.4 的报错示例"Couldn't parse: 'C - 53.08\n'"中就可以看到repr(line)输出的换行符,这正是对原始行内容的直观展示。
3. 类型转换的边界:遇到坏数据会怎样?
int()和float()在无法转换时会抛出ValueError。例如 Data/portfolio3.dat 中混入了C - 53.08这类第二列为-的坏行,直接转换会崩溃。这一"崩溃点"正是 练习 1.4 的主题:把pcost.py重构为函数portfolio_cost(filename),并用try/except ValueError捕获坏行、打印警告后继续处理。仓库中的演进版本 Solutions/1_4/pcost.py 展示了这一改造的完整代码,可作为"从脚本走向健壮程序"的过渡参考。
进阶延伸:从脚本到可复用函数
虽然练习 1.3 只要求写出可运行的脚本,但紧接着的 练习 1.4 会要求你把它改造成带参函数:
def portfolio_cost(filename): total_cost = 0.0 with open(filename) as f: for line in f: fields = line.split() nshares = int(fields[1]) price = float(fields[2]) total_cost = total_cost + nshares * price return total_cost print(portfolio_cost('Data/portfolio.dat'))改造后的函数可以直接在交互解释器中调用,例如用python3 -i pcost.py进入交互模式后执行:
>>> portfolio_cost('Data/portfolio.dat') 44671.15 >>> portfolio_cost('Data/portfolio2.dat') 19908.75其中 Data/portfolio2.dat 是另一份四行记录的测试数据(AA 50 27.10、HPQ 250 43.15、MSFT 25 50.15、GE 125 52.10),可作为函数正确性的第二个验证样例。
仓库中的可参考资源
- 练习原文:Exercises/ex1_3.md
- 官方解答:Exercises/soln1_3.md
- 可运行实现:Solutions/1_3/pcost.py
- 练习输入数据:Data/portfolio.dat、Data/portfolio2.dat、Data/portfolio3.dat(含坏数据,供练习 1.4 使用)
- 后续演进:Solutions/1_4/pcost.py(函数化 + 异常处理)、Exercises/ex1_4.md
- 课程总览:README.md、PythonMastery.pdf(配套讲义)
小结
练习 1.3 用 10 行左右的代码,完整覆盖了 Python 文件 I/O 的最小闭环:open→ 逐行迭代 →split切分 → 数值转换 → 累加求和。这不仅是本课程所有数据处理类练习(后续的 CSV 读取、实时日志跟踪等)的基础,也是日常脚本开发中使用频率最高的核心模式。建议亲手在仓库根目录写下pcost.py并跑通44671.15这个结果,再按 练习 1.4 的要求把它演进为带异常处理的函数,完成从"能跑"到"健壮"的第一次升级。
- 示例工程
- 教程
【免费下载链接】python-mastery
Advanced Python Mastery (course by @dabeaz)
相关推荐
5步构建Python股票数据分析平台:从入门到实战的完整指南 🚀
5步构建Python股票数据分析平台:从入门到实战的完整指南 🚀 想要掌握Python高级编程技巧?通过构建一个 股票数据分析平台 ,你不仅能学习Python
示例工程教程Practical Python投资分析工具:pcost.py计算秘籍
Practical Python投资分析工具:pcost.py计算秘籍 想要掌握Python投资分析的核心技能吗?🤔 pcost.py作为Practical
教程文档7个Python算法优化技巧:从O(n²)到O(n log n)的性能蜕变
7个Python算法优化技巧:从O n² 到O n log n 的性能蜕变 在Python编程中,算法效率往往决定了程序的性能上限。 GitHub 加速计划 /
示例工程教程
创作声明:本文部分内容由AI辅助生成(AIGC),仅供参考