简介
如果你要编写一个较大的程序,你并不真想将其组织成顶层的大量独立文件集合。本节介绍包的概念。
This tutorial is from open-source community. Access the source code
💡 本教程由 AI 辅助翻译自英文原版。如需查看原文,您可以 切换至英文原版
如果你要编写一个较大的程序,你并不真想将其组织成顶层的大量独立文件集合。本节介绍包的概念。
任何 Python 源文件都是一个模块。
## foo.py
def grok(a):
...
def spam(b):
...
import
语句会加载并执行一个模块。
## program.py
import foo
a = foo.grok(2)
b = foo.spam('Hello')
...
对于更大的代码集合,将模块组织成包是很常见的做法。
## 从这样
pcost.py
report.py
fileparse.py
## 到这样
porty/
__init__.py
pcost.py
report.py
fileparse.py
你选择一个名称并创建一个顶级目录。 上面示例中的 porty
(显然,选择这个名称是最重要的第一步)。
在该目录中添加一个 __init__.py
文件。它可以是空的。
将你的源文件放入该目录中。
包用作导入的命名空间。
这意味着现在有了多级导入。
import porty.report
port = porty.report.read_portfolio('portfolio.csv')
导入语句还有其他变体。
from porty import report
port = report.read_portfolio('portfolio.csv')
from porty.report import read_portfolio
port = read_portfolio('portfolio.csv')
这种方法存在两个主要问题。
所以,基本上所有东西都会出错。不过,除此之外,它还是能运行的。
同一包内文件之间的导入现在必须在导入中包含包名。记住这个结构。
porty/
__init__.py
pcost.py
report.py
fileparse.py
修改后的导入示例。
from porty import fileparse
def read_portfolio(filename):
return fileparse.parse_csv(...)
所有导入都是绝对的,而非相对的。
import fileparse ## 出错。找不到 fileparse
...
你可以使用 .
来指代当前包,而不是直接使用包名。
from. import fileparse
def read_portfolio(filename):
return fileparse.parse_csv(...)
语法:
from. import modname
这样便于重命名包。
将包的子模块作为主脚本运行会出错。
$ python porty/pcost.py ## 出错
...
原因:你在单个文件上运行Python,而Python无法正确识别包结构的其余部分(sys.path
有误)。
所有导入都会出错。要解决这个问题,你需要以不同的方式运行程序,使用 -m
选项。
$ python -m porty.pcost ## 可行
...
__init__.py
文件这些文件的主要目的是将模块组合在一起。
示例:整合函数
## porty/__init__.py
from.pcost import portfolio_cost
from.report import portfolio_report
这使得在导入时,名称出现在顶级。
from porty import portfolio_cost
portfolio_cost('portfolio.csv')
而不是使用多级导入。
from porty import pcost
pcost.portfolio_cost('portfolio.csv')
如前所述,现在你需要使用 -m package.module
来运行包内的脚本。
$ python3 -m porty.pcost portfolio.csv
还有另一种选择:编写一个新的顶级脚本。
#!/usr/bin/env python3
## pcost.py
import porty.pcost
import sys
porty.pcost.main(sys.argv)
此脚本位于包外部。例如,查看目录结构:
pcost.py ## 顶级脚本
porty/ ## 包目录
__init__.py
pcost.py
...
代码组织和文件结构是应用程序可维护性的关键。
对于Python来说,没有“一刀切”的方法。然而,一种适用于许多问题的结构大致如下。
porty-app/
README.txt
script.py ## 脚本
porty/
## 库代码
__init__.py
pcost.py
report.py
fileparse.py
顶级目录 porty-app
是其他所有内容的容器 —— 文档、顶级脚本、示例等等。
同样,顶级脚本(如果有的话)需要存在于代码包之外,即上一级目录。
#!/usr/bin/env python3
## porty-app/script.py
import sys
import porty
porty.report.main(sys.argv)
此时,你有一个包含几个程序的目录:
pcost.py ## 计算投资组合成本
report.py ## 生成报告
ticker.py ## 生成实时股票报价
还有各种具有其他功能的支持模块:
stock.py ## 股票类
portfolio.py ## 投资组合类
fileparse.py ## CSV解析
tableformat.py ## 格式化表格
follow.py ## 跟踪日志文件
typedproperty.py ## 带类型的类属性
在本练习中,我们将清理代码并将其放入一个通用包中。
创建一个名为 porty/
的目录,并将上述所有Python文件放入其中。此外,创建一个空的 __init__.py
文件并将其放入该目录中。你应该有一个如下所示的文件目录:
porty/
__init__.py
fileparse.py
follow.py
pcost.py
portfolio.py
report.py
stock.py
tableformat.py
ticker.py
typedproperty.py
删除你目录中存在的 __pycache__
文件。这包含之前预编译的Python模块。我们希望重新开始。
尝试导入一些包模块:
>>> import porty.report
>>> import porty.pcost
>>> import porty.ticker
如果这些导入失败,请进入相应的文件并修复模块导入,以包含相对包的导入。例如,像 import fileparse
这样的语句可能会更改为以下内容:
## report.py
from. import fileparse
...
如果你有像 from fileparse import parse_csv
这样的语句,将代码更改为以下内容:
## report.py
from.fileparse import parse_csv
...
对于一个应用程序来说,仅仅把所有代码放入一个“包”中通常是不够的。有时候还会有支持文件、文档、脚本以及其他内容。这些文件需要存在于你在上面创建的 porty/
目录之外。
创建一个名为 porty-app
的新目录。将你在练习9.1中创建的 porty
目录移动到该目录中。将 portfolio.csv
和 prices.csv
测试文件复制到这个目录中。另外创建一个 README.txt
文件,写入一些关于你自己的信息。现在你的代码应该如下组织:
porty-app/
portfolio.csv
prices.csv
README.txt
porty/
__init__.py
fileparse.py
follow.py
pcost.py
portfolio.py
report.py
stock.py
tableformat.py
ticker.py
typedproperty.py
要运行你的代码,你需要确保你在顶级目录 porty-app/
中工作。例如,在终端中:
$ cd porty-app
$ python3
>>> import porty.report
>>>
尝试将你之前的一些脚本作为主程序运行:
$ cd porty-app
$ python3 -m porty.report portfolio.csv prices.csv txt
Name Shares Price Change
---------- ---------- ---------- ----------
AA 100 9.22 -22.98
IBM 50 106.28 15.18
CAT 150 35.46 -47.98
MSFT 200 20.89 -30.34
GE 95 13.48 -26.89
MSFT 50 20.89 -44.21
IBM 100 106.28 35.84
$
使用 python -m
命令通常有点奇怪。你可能想要编写一个顶级脚本来处理包的一些特殊情况。创建一个生成上述报告的脚本 print-report.py
:
#!/usr/bin/env python3
## print-report.py
import sys
from porty.report import main
main(sys.argv)
将此脚本放在顶级目录 porty-app/
中。确保你可以在该位置运行它:
$ cd porty-app
$ python3 print-report.py portfolio.csv prices.csv txt
Name Shares Price Change
---------- ---------- ---------- ----------
AA 100 9.22 -22.98
IBM 50 106.28 15.18
CAT 150 35.46 -47.98
MSFT 200 20.89 -30.34
GE 95 13.48 -26.89
MSFT 50 20.89 -44.21
IBM 100 106.28 35.84
$
你最终的代码现在应该如下结构:
porty-app/
portfolio.csv
prices.csv
print-report.py
README.txt
porty/
__init__.py
fileparse.py
follow.py
pcost.py
portfolio.py
report.py
stock.py
tableformat.py
ticker.py
typedproperty.py
恭喜你!你已经完成了“包”实验。你可以在LabEx中练习更多实验来提升你的技能。