Python Mastery 实战解析:模块定义与 import 语句——以 Exercise 1.6 为核心深入理解 Python 模块机制
示例工程教程【免费下载链接】python-masteryAdvanced Python Mastery (course by dabeaz)项目地址https://gitcode.com/gh_mirrors/py/python-mastery点击查看免费下载本篇技术指南以 Advanced Python Mastery 课程的 Exercise 1.6 为骨架系统讲解 Python 中模块的定义方式、import语句的加载语义以及if __name__ __main__主模块保护这一核心实践。文中所有代码与数据均取自当前仓库的真实文件Solutions/1_6/pcost.py、Data/portfolio.dat 等读者完成后将掌握如何把前几节写好的函数与类封装成可导入的模块、如何在交互式解释器中调用模块功能以及如何让同一份代码既可作为脚本运行又可作为库被复用。一、练习目标与前置环境准备本练习聚焦两个目标定义模块Defining modules使用 import 语句Using the import statement与之前练习不同本练习不创建新文件Files Created: None只修改上一节生成的pcost.py。它的前提是你在 Exercise 1.4 中把pcost.py改造成了带异常处理的portfolio_cost(filename)函数并在 Exercise 1.5 中创建了stock.py的Stock类。环境检查sys.path 与解释器重启文档在开头特别强调做模块相关练习时运行环境是否正确至关重要。如果import语句总是失败第一件事就是检查sys.path import sys sys.path [, /usr/local/lib/python3.11, ...]列表中的空字符串代表当前工作目录Python 导入模块时会优先从这里查找如果你把pcost.py、stock.py放在了一个奇怪的目录如桌面下某个深层文件夹而解释器启动目录不包含它import就会抛出ModuleNotFoundError解决办法在存放这些.py文件的同一目录下启动 Python或者把该目录追加进sys.path如sys.path.append(/path/to/your/files)。同时练习要求先重启解释器会话在 IDLE 中执行 Shell Restart Shell会看到################## RESTART ##################提示在 Unix 环境下直接退出再重启 Python。这样做的意义在于保证随后的导入行为是全新加载避免之前会话残留的命名空间干扰实验结论。二、第 (a) 部分用 import 语句加载已有程序在之前练习中你已经写出了两个程序pcost.py与stock.py。现在通过import把它们当作模块加载并直接复用其功能 import pcost 44671.15 pcost.portfolio_cost(Data/portfolio2.dat) 19908.75 from stock import Stock s Stock(GOOG, 100, 490.10) s.name GOOG s.cost() 49010.0 逐行解读这段会话背后是三条核心机制import pcost会执行模块的全部顶层代码。当前仓库中 Solutions/1_6/pcost.py 是带__name__保护后的版本而练习开始时你的pcost.py末尾还留有 Exercise 1.4 要求添加的无条件调用print(portfolio_cost(Data/portfolio.dat))——所以第一次import pcost才会打印出44671.15这正是 Data/portfolio.dat 的核算结果见下文第五节。这是理解__name__保护必要性的关键动机。pcost.portfolio_cost(Data/portfolio2.dat)是模块名.函数名的属性访问。函数定义在模块顶层导入后即可通过模块对象调用。注意这里的文件路径Data/portfolio2.dat是相对于当前工作目录的而不是相对于模块文件所在目录因为运行时是解释器进程负责打开文件。from stock import Stock把名字直接注入当前命名空间。与import stock需要stock.Stock(...)访问不同from ... import ...让你直接用Stock构造对象。练习中的s.cost()返回49010.0对应100 股 × 490.10 元与 Solutions/1_5/stock.py 中Stock.cost()的实现完全吻合。如果导入失败怎么办文档给出的诊断路径非常实用确认运行目录确保 Python 启动目录与.py文件所在目录一致os.getcwd()可查看确认 sys.path 覆盖若目录不一致要么cd过去要么把目录加入sys.path还可通过print(pcost.__file__)查看模块实际加载自哪个文件用于排查导入到了旧版本文件的问题。三、第 (b) 部分Main Module——if __name__ __main__保护问题场景你的pcost.py最后一行是无条件执行的print(portfolio_cost(Data/portfolio.dat))。作为脚本运行时这没问题但一旦被别人import pcost这行打印也会跟着执行——模块被导入时产生副作用输出这在真实工程中是不可接受的。修改方案在 Exercise 1.6 的官方解答 与仓库参考实现 Solutions/1_6/pcost.py 中完整的pcost.py如下# pcost.py def portfolio_cost(filename): total_cost 0.0 with open(filename) as f: for line in f: fields line.split() try: nshares int(fields[1]) price float(fields[2]) total_cost total_cost nshares * price # This catches errors in int() and float() conversions above except ValueError as e: print(Couldnt parse:, line) print(Reason:, e) return total_cost if __name__ __main__: print(portfolio_cost(Data/portfolio.dat))关键改动只有一处把末尾的无条件打印缩进放进if __name__ __main__:分支内。函数定义部分原封不动异常处理except ValueError打印坏行与原因也完整保留。两种运行方式的验证方式一作为主程序运行脚本模式bash % python3 pcost.py 44671.25 bash %方式二作为库导入模块模式——不应有任何输出 import pcost 第二次导入与第一次第 (a) 部分末尾未修改版本的对比一目了然修改后import pcost静默完成只加载函数定义不再打印任何东西。原理深挖__name__到底是什么每个模块在运行时都有一个内置变量__name__当模块作为主程序被python3 pcost.py执行时解释器把它命名为__main__于是if __name__ __main__:为真打印执行当模块被import pcost导入时__name__被设置为模块名pcost条件为假分支跳过因此这行保护把顶层执行代码与可复用的定义隔离开来让同一文件身兼两职既能命令行直跑又能被安全导入。需要补充的工程常识是import对同一模块只会执行一次顶层代码后续导入直接复用sys.modules中的缓存对象这也是重启解释器后再做本练习能观察到一致行为的原因之一。四、从模块到复用为什么这是 Python 工程的基石把代码拆成模块并加上__name__保护是 Python 从脚本走向程序库的第一步其收益在课程后续练习中被反复放大Exercise 3.3 起出现的共享模块reader.py把 CSV/数据文件读取逻辑从各脚本中抽离多个脚本共同import reader参考 Solutions/3_3/reader.pyExercise 9.3 更进一步把模块组织成包packagestructly内部再划分tableformat子包与formats格式插件目录见 Solutions/9_3/structlyteststock.py如 Solutions/6_1/teststock.py则直接from stock import Stock后编写单元测试——这正是本练习from stock import Stock语法在真实测试代码中的典型用法。可以说本节的import、from ... import ...、if __name__ __main__三者组合构成了后续所有可测试、可复用代码的组织基础。五、数据核算用仓库真实数据验证结果为了让输出可复现这里把两个数据文件的账目完整核算一遍。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逐行累加份额 × 单价股票份额单价小计AA10032.203220.00IBM5091.104555.00CAT15083.4412516.00MSFT20051.2310246.00GE9540.373835.15MSFT5065.103255.00IBM10070.447044.00合计44671.15Data/portfolio2.dat4 行记录AA 50 27.10 HPQ 250 43.15 MSFT 25 50.15 GE 125 52.10合计为50×27.10 250×43.15 25×50.15 125×52.10 1355.00 10787.50 1253.75 6512.50 19908.75。一个值得注意的事实核对交互式会话中pcost.portfolio_cost(Data/portfolio2.dat)返回19908.75与上面核算完全一致而练习文档第 (b) 部分示例显示运行python3 pcost.py输出44671.25按当前仓库的 Data/portfolio.dat 实际核算应为44671.15与第 (a) 部分交互式import pcost打印的结果相同。可以推断44671.25对应的是课程编写时期旧版本数据文件以你本地仓库数据实测为准。这个小细节恰好说明动手运行永远比照抄文档数字更可靠。六、练习自检清单完成本练习后你可以对照以下清单确认自己真正掌握了模块机制能否在交互式解释器中执行import pcost且不产生任何输出证明__name__保护生效能否用pcost.portfolio_cost(Data/portfolio.dat)拿到44671.15能否用from stock import Stock创建对象并调用cost()得到49010.0当import失败时是否知道通过sys.path与工作目录定位问题是否理解import会执行模块顶层代码与__name__在两种运行方式下取值不同这两条底层机制如果全部通过说明你已经具备了 Python 模块化开发的基础能力接下来的课程将在这个基础上把模块演化为包、引入验证器与元编程等高级主题。本文基于 Exercises/ex1_6.md 及仓库内对应解答 Exercises/soln1_6.md、参考实现 Solutions/1_6/pcost.py 与数据文件编写源码与数据均可在当前仓库中直接查阅与复现。赞分享示例工程教程【免费下载链接】python-masteryAdvanced Python Mastery (course by dabeaz)项目地址https://gitcode.com/gh_mirrors/py/python-mastery点击查看免费下载相关推荐Python 包化实战将模块重构为 structly 包Advanced Python Mastery Exercise 9.2Python 包化实战将模块重构为 structly 包Advanced Python Mastery Exercise 9.2 导读 本篇文章对应 A示例工程教程python-mastery 课程实战用 if __name__ __main__ 守卫把 Python 脚本改造成可复用模块Exercise 1.6 详解python mastery 课程实战用 if __name__ __main__ 守卫把 Python 脚本改造成可复用模块Exercise 1示例工程教程Python模块与包管理终极指南import机制深度剖析与实战技巧Python模块与包管理终极指南import机制深度剖析与实战技巧 Python模块与包管理是每个Python开发者必须掌握的核心技能无论你是Python初示例工程教程创作声明:本文部分内容由AI辅助生成(AIGC),仅供参考