翻译:《实用的Python编程》09_01_Packages

如果编写一个较大的程序,我们并不真的想在顶层将其组织为一个个独立文件的大型集合。本节对包(package)进行介绍。

模块

任何一个 Python 源文件称为一个模块(module)。

# foo.py def grok(a): ... def spam(b): ...

一条 import 语句加载并执行 一个模块。

# program.py import foo a = foo.grok(2) b = foo.spam('Hello') ... 包 vs 模块

对于较大的代码集合,通常将模块组织到包中。

# From this pcost.py report.py fileparse.py # To this porty/ __init__.py pcost.py report.py fileparse.py

首先,选择一个名字并用该名字创建顶级目录。如上述的 porty (显然,第一步最重要的是选择名字)。

接着,添加 __init__.py 文件到该目录中。__init__.py 文件可以是一个空文件。

最后,把源文件放到该目录中。

使用包

包用作导入的命名空间。

这意味着现在有了多级导入。

import porty.report port = porty.report.read_portfolio('port.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

根据上述规则(同一包内的不同文件之间的导入必须包含包名)修改后的导入示例:

# report.py from porty import fileparse def read_portfolio(filename): return fileparse.parse_csv(...)

所有的导入都是绝对的,而不是相对的。

# report.py import fileparse # BREAKS. fileparse not found ... 相对导入

除了使用包名直接导入,还可以使用使用 . 引用当前的包。

# report.py from . import fileparse def read_portfolio(filename): return fileparse.parse_csv(...)

语法:

from . import modname

使用上述语法使得重命名包变得容易。

问题:主脚本

将包内的子模块作为主脚本运行会导致程序中断:

bash $ python porty/pcost.py # BREAKS ...

原因:你正在运行单个脚本,而 Python 不知道包的其余部分(sys.path 是错误的)。

所有的导入都会中断。要想解决这个问题,需要以不同的方式运行程序,可以使用 -m 选项。

bash $ python -m porty.pcost # WORKS ... __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 运行包内的脚本。

bash % python3 -m porty.pcost portfolio.csv

还有另一种选择:编写一个新的顶级脚本。

#!/usr/bin/env python3 # pcost.py import porty.pcost import sys porty.pcost.main(sys.argv)

脚本位于包外面。目录结构如下:

pcost.py # top-level-script porty/ # package directory __init__.py pcost.py ... 应用结构

代码组织和文件结构是应用程序可维护性的关键。

对于 Python 而言,没有“放之四海而皆准”的方法,但是一个适用于多种问题的结构就是这样:

porty-app/ README.txt script.py # SCRIPT porty/ # LIBRARY CODE __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 # computes portfolio cost report.py # Makes a report ticker.py # Produce a real-time stock ticker

同时,还有许多具有各种功能的支持模块:

stock.py # Stock class portfolio.py # Portfolio class fileparse.py # CSV parsing tableformat.py # Formatted tables follow.py # Follow a log file typedproperty.py # Typed class properties

在本次练习中,我们将整理这些代码并将它们放入一个通用包中。

练习 9.1:创建一个简单的包

请创建一个名为 porty 的目录并将上述所有的 Python 文件放入其中。另外,在 porty 目录中创建一个空的 __init__.py 文件。最后,文件目录看起来像这样:

porty/ __init__.py fileparse.py follow.py pcost.py portfolio.py report.py stock.py tableformat.py ticker.py typedproperty.py

请将 porty 目录中的 __pycache__ 目录移除。该目录包含了之前预编译的 Python 模块。我们想重新开始。

尝试导入包中的几个模块:

>>> import porty.report >>> import porty.pcost >>> import porty.ticker

内容版权声明:除非注明,否则皆为本站原创文章。

转载注明出处:https://www.heiqu.com/wppzdd.html