0
点赞
收藏
分享

微信扫一扫

Python 开发的现代方法

作为专家,最重要的是要对所选用的编程语言有深刻的理解。对于任何技术来说都是

如此。但如果不知道在特定语言社区中的常用工具和实践的话,想开发一款好软件是相当

困难的。Python 所有的单项功能都可以在其他某种语言中找到。所以,直接比较语法、表

现力(expressiveness)或性能的话,总会在一个或多个方面存在更好的解决方案。但 Python

真正出众的领域在于围绕语言打造的整个生态系统。多年来,Python 社区完善了标准实践

和标准库,有助于在更短的时间内创建更可靠的软件。

对于上文提到的生态系统,最明显也最重要的一部分就是大量免费的开源包,可以用

来解决许多问题。编写新软件总是一个费钱又费时的过程。能够复用现有代码而无需重新

造轮子(reinvent the wheel),可以大大降低开发的时间和成本。这也是某些公司的项目在

经济上可行的唯一原因。

由于这个原因,Python 开发者花费大量精力来创建工具和标准,方便使用他人创建的开

源包。我们首先介绍虚拟隔离环境、改进的交互式 shell 和调试器,然后介绍一些程序,有助

于发现、搜索和分析 PyPIPython Package Index,Python 包索引)上大量可用的 Python 包。

应用层 Python 环境隔离

现在许多操作系统都将 Python 作为标准组件。对于大多数 Linux 发行版和基于 Unix

的系统(如 FreeBSD、NetBSD、OpenBSD 或 OS X 系统)来说,要么默认安装了 Python,

要么系统软件包仓库中包含 Python。其中很多系统甚至将 Python 作为核心组件的一部分。

有些操作系统的安装程序是用 Python 编写的,例如 Ubuntu 系统的 Ubiquity、Red Hat Linux

和 Fedora 系统的 Anaconda。

基于这一事实,PyPI 上的许多包也可以用系统包管理工具(如 Debian 和 Ubuntu 的

apt-get、Red Hat Linux 的 rpm、Gentoo 的 emerge)作为本地包来管理。不过应该记住,

可用的库非常有限,大部分也比 PyPI 上的版本要旧。因此,PyPA(Python Packaging

Authority,Python 包官方小组)推荐始终采用 pip 来获取最新版本的 Python 包。虽然从

CPython 2.7.9 版和 3.4 版开始,pip 已经成为一个独立的 Python 包,但每一个新版本都会

默认安装 pip。安装新 Python 包的方法就是这么简单,如下所示:

pip install <package-name>

pip 功能十分强大,可以强制安装特定版本的 Python 包(语法为 pip install

package-name==version),或升级到最新可用的版本(使用--upgrade 参数)。对于

本书中提到的大多数命令行工具来说,在命令后添加-h 或--help 参数并运行可以轻松获

得其完整的用法说明,但下面给出一个示例会话,展示其最常用的选项:

$ pip show pip

---

Metadata-Version: 2.0

Name: pip

Version: 7.1.2

Summary: The PyPA recommended tool for installing Python packages.

Home-page: https://pip.pypa.io/

Author: The pip developers

Author-email: python-virtualenv@groups.google.com

License: MIT

Location: /usr/lib/python2.7/site-packages

Requires:

$ pip install 'pip<7.0.0'

Collecting pip<7.0.0

Downloading pip-6.1.1-py2.py3-none-any.whl (1.1MB)

100% |████████████████████████████████| 1.1MB 242kB/s

Installing collected packages: pip

Found existing installation: pip 7.1.2

Uninstalling pip-7.1.2:

Successfully uninstalled pip-7.1.2

Successfully installed pip-6.1.1

You are using pip version 6.1.1, however version 7.1.2 is available.

You should consider upgrading via the 'pip install --upgrade pip'

command.

$ pip install --upgrade pip

You are using pip version 6.1.1, however version 7.1.2 is available.

You should consider upgrading via the 'pip install --upgrade pip'

command.

Collecting pip

Using cached pip-7.1.2-py2.py3-none-any.whl

Installing collected packages: pip

Found existing installation: pip 6.1.1

Uninstalling pip-6.1.1:

Successfully uninstalled pip-6.1.1

Successfully installed pip-7.1.2

在某些情况下,可能默认 pip 不可用。从 Python 3.4 版和 2.7.9 版开始,总是可以使

用 ensurepip 模块来引导启动 pip,具体如下:

$ python -m ensurepip

Ignoring indexes: https://pypi.python.org/simple

Requirement already satisfied (use --upgrade to upgrade): setuptools in /

usr/lib/python2.7/site-packages

Collecting pip

Installing collected packages: pip

Successfully installed pip-6.1.1

关于在旧版 Python 中如何安装 pip 的方法,访问项目的文档页面可获取最新信息。

为什么要隔离

pip 可用于安装系统级的 Python 包。在基于 Unix 的系统和 Linux 系统上,这么做需

要超级用户权限,所以实际的调用如下所示:

sudo pip install <package-name>

注意,在 Windows 上并不需要这样做,因为没有默认安装 Python 解释器,Windows

上的 Python 通常由用户手动安装,无需超级用户权限。

无论如何,不推荐直接从 PyPI 安装系统级的 Python 包,也应尽量避免这一做法。前

面说 PyPA 推荐使用 pip,这似乎与前面的说法相矛盾,但其中是有很重要的原因。如前

所述,通过操作系统软件包仓库,Python 往往是许多软件包的重要组成部分,也可以提供

许多重要服务。系统发行版的维护者投入大量精力选择合适的软件包版本,以匹配各种包

依赖。通常来说,系统软件包仓库中的 Python 包都包含自定义补丁,或者使用较旧的版本,

只是为了保证与其他系统组件的兼容。利用 pip 将这些 Python 包强制更新至某一版本,打

破了向后兼容,也可能会破坏某些关键的系统服务。

即使在本地计算机上,为了方便开发而做这些事情也不是一个好的理由。那样胡乱使

用 pip 几乎总会引起麻烦,最终导致难以调试的问题。并不是说要严格禁止从 PyPI 全局

安装 Python 包,但这么做时一定要清楚地认识到相关风险。

幸运的是,这个问题有一个简单的解决方案,就是环境隔离。在不同的系统抽象层中

对 Python 运行环境进行隔离的工具有很多种。其主要作用是,将项目依赖与其他项目和/

或系统服务需要的包进行隔离。这种方法的好处在于以下几个方面。

• 解决了这样的难题:“X 项目依赖于 1.x 版,但 Y 项目却需要 4.x 版”。开发人员可

以同时开发多个项目,这些项目的依赖不同,甚至可能相互冲突,但项目之间却不

会相互影响。

• 项目不再受限于系统发行版仓库中包的版本。

• 不会破坏依赖特定包版本的其他系统服务,因为新版软件包只存在于隔离环境

内部。

• 项目依赖的包列表可以轻松“锁定(frozen)”,复制起来也很容易。

隔离最简单也最轻便的方法就是使用应用层的虚拟环境。它们仅隔离 Python 解释器和

其中可用的 Python 包。其设置非常简单,通常也足以保证小项目和小软件包开发过程中的

隔离。

不幸的是,在某些情况下,这种做法可能不足以保证充分的一致性和可重复性。对于

这种情况,系统级隔离是对工作流程很好的补充,本章后面也会介绍一些可用的方案。

常用解决方案

在运行时隔离 Python 的方法有几种。最简单也最显而易见的方法,就是手动修改 PATH

和 PYTHONPATH 环境变量或将 Python 二进制文件移动到其他位置,以改变它发现可用

Python 包的方式,将环境变量修改成保存项目依赖的自定义位置,当然这种方法也最难维

护。幸运的是,有几种工具可以帮助维护虚拟环境,并维护系统中安装包的存储方式。这

些工具主要包括:virtualenv、venv 和 buildout。它们在底层做的事情实际上与我

们手动做的一样。实际的策略取决于具体的工具实现。但一般来说,它们更方便使用,而

且还有其他好处。

1.virtualenv

在这个工具列表中,virtualenv 是目前最常用的工具。它名字的含义就是虚拟环境

(virtual environment)。它并不是 Python 标准发行版的一部分,所以需要用 pip 来获取。它

也是值得在系统层面安装的 Python 包之一(在 Linux 系统和基于 Unix 的系统中要用到

sudo)。

安装完成后,利用下面的命令可以创建一个新的虚拟环境:

virtualenv ENV

这里的 ENV 应替换为新环境的名字。这将在当前工作目录路径中创建一个新的 ENV

目录。里面包含以下几个新目录。

• bin/:里面包含新的 Python 可执行文件和其他包提供的脚本/可执行文件。

• lib/和 include/:这些目录包含虚拟环境中新 Python 的支持库文件。新的 Python

包将会安装在 ENV/lib/pythonX.Y/site-packages/中。

创建好新环境后,需要用 Unix 的 source 命令在当前 shell 会话中激活它:

source ENV/bin/activate

这将会影响环境变量,从而改变当前 shell 会话的状态。为了告知用户已经激活了虚拟环

境,shell 提示符会在开头增加(ENV)字符串。下面举个例子,在会话中创建一个新环境并激活:

$ virtualenv example

New python executable in example/bin/python

Installing setuptools, pip, wheel...done.

$ source example/bin/activate

(example)$ deactivate

$

关于 virtualenv 要注意,最重要的是它完全依赖于在文件系统中的存储状态。它不

会提供额外功能来跟踪应该安装哪些包。这些虚拟环境不可移植,不能移动到其他系统或

机器。对每个新的应用部署来说,都需要从头开始创建新的虚拟环境。因此,virtualenv

用户有一个良好实践,就是将所有项目依赖保存到一个 requirements.txt 文件(约定

命名)中,正如下面的代码所示:

# 井号(#)后面的内容是注释。

明确版本号,可重复性高。

graceful==0.1.1

# 如果项目在不同依赖版本中都通过测试,

# 也可以指定相对版本编号。

falcon>=0.3.0,<0.5.0

# 应尽量明确 Python 包的版本,

# 除非始终需要最新版。

pytz

有了这个文件,用 pip 就可以轻松安装所有依赖,因为它可以接受需求文件作为参数:

pip install -r requirements.txt

需要记住,需求文件并不总是理想的解决方案,因为它没有给定依赖的准确列表,而

只给出了需要安装的依赖。因此,如果需求文件并非最新版,无法反映环境的实际状态,

那么整个项目在开发环境中可以正常运行,但在其他环境中却无法启动。当然,pip

freeze 命令可以打印出当前环境所有的 Python 包,但不应该盲目使用这个命令。它会打

印出所有内容,甚至那些仅用于测试而并不用于项目的 Python 包。本书提到的另一款工具

buildout 就解决了这个问题,所以可能是某些开发团队的更佳选择。

2.venv

虚拟环境很快逐步完善,成为了社区中的常用工具。从 Python 3.3 开始,标准库已经

支持创建虚拟环境。其用法与 Virtualenv 几乎相同,虽然命令行选项采用了不同的命名约

定。新的 venv 模块提供了 pyvenv 脚本,可以用于创建新的虚拟环境:

pyvenv ENV

这里的 ENV 应替换为新环境的名字。此外,现在也可以用 Python 代码直接创建新的环境,因为所有功能都包含在内置的 venv 模块中。其他用法和实现细节(例如环境目录

的结构、激活/关闭脚本)与 Virtualenv 几乎完全相同,所以换用这种方法应该很简单,也

不会牵扯太多精力。

对于使用较新版本 Python 的开发人员来说,推荐使用 venv 而不是 Virtualenv。对于

Python 3.3 版,切换到 venv 可能需要付出更多的精力,因为这一版本在新环境中没有默认

安装 setuptools 和 pip,所以用户需要手动安装它们。幸运的是,这一点在 Python 3.4

中已经修改,并且由于 venv 的可定制性,其内容可以被改写。对于细节的解释可参见 Python

文档,但有些用户可能会认为它过于复杂,仍然在这一版本的 Python 中继续使用 Virtualenv。

3.buildout

buildout 是一个强大工具,可与引导启动并部署用 Python 编写的应用。它的一些高级

特性将在本书后面讲到。在很长一段时间内,他还被用作创建 Python 隔离环境的工具。由

于 buildout 需要声明性的配置,每次依赖发生变化都必须修改配置,因此这些环境更容易

复制和管理,无需依赖环境状态。

不幸的是,这一情况已发生变化。从 2.0.0 版开始,buildout 包不再提供与系统 Python

在任何层级的隔离。处理隔离的任务留给其他工具来做,如 virtualenv,所以仍然可以用 buildout

来做隔离,但事情变得有点复杂。buildout 必须要在隔离环境中初始化才能真正隔离。

与之前版本的 buildout 相比,这一版本有一个主要缺点,就是它要依赖其他隔离方法。

开发这些代码的开发人员不再确定对依赖的描述是否完整,因为有些 Python 包可以绕过声

明性配置来安装。当然,这个问题可以通过适当的测试和发布过程来解决,但却使整个工

作流程更加复杂。

总而言之,buildout 不再是提供环境隔离的解决方案,但其声明性配置可以提高虚拟环

境的可维护性和可重复性。

举报

相关推荐

0 条评论