Python 高级编程(第2版)

本书全面深入地探讨了 Python 3 的高级编程技巧,覆盖了语言现状、语法最佳实践、命名规范、包的创建、代码部署、扩展开发、环境隔离、文档编写、测试驱动开发等多个关键主题。旨在帮助开发者提升 Python 技能,创建高性能、可靠且可维护的软件。适合有一定 Python 基础,希望进一步提高编程水平的专业人士,尤其对 Web 开发者和后端工程师极具参考价值。
摘要由CSDN通过智能技术生成
内容简介

Python 作为一种高级程序设计语言,凭借其简洁、易读及可扩展性日渐成为程序设计领域备受推崇的语言之一。

本书基于 Python 3.5 版本进行讲解,深度揭示了 Python 编程的高级技巧。本书从 Python 语言及其社区的现状开始介绍,对 Python 语法、命名规则、Python 包的编写、部署代码、扩展程序开发、管理代码、文档编写、测试开发、代码优化、并发编程、设计模式等重要话题进行了全面系统化的讲解。

本书适合想要进一步提高自身 Python 编程技能的读者阅读,也适合对 Python 编程感兴趣的读者参考学习。全书结合典型且实用的开发案例,可以帮助读者创建高性能的、可靠且可维护的 Python 应用。

作者简介

Michał Jaworski 有着7年 Python 编程的经验。他还是 graceful 的创建者,这是一个构建于 falcon 之上的 REST 框架。他曾在不同的公司担任过多种角色,从一名普通的全栈开发人员到软件架构师再到一家快节奏创业公司的工程副总裁。他目前是 Opera 软件公司 TV Store(电视应用商店)团队的首-席后端工程师。他在设计高性能的分布式服务方面拥有丰富的经验。他还是一些流行的 Python 开源项目的活跃贡献者。

Tarek Ziadé 是 Mozilla 的工程经理,与一个专门用 Python 为 Firefox 构建大规模 Web 应用的团队合作。他对 Python 打包做出过贡献,而且从早期 Zope 开始就使用过各种不同的 Python Web 框架。

本书内容
前言

Python 很棒!

从20世纪80年代末出现的最早版本到当前版本,Python 的发展一直遵循着相同的理念:提供一个同时具备可读性和生产力的多范式编程语言。

人们曾经将 Python 看作另一种脚本语言,认为它不适合构建大型系统。但多年以来,在一些先驱公司的努力下,Python 显然可以用于构建几乎任何类型的系统。

实际上,许多其他语言的开发者也醉心于 Python,并将它作为首选语言。

如果你购买了这本书,可能已经知道这些内容了,所以无需再向你证明这门语言的优点。

本书展现了作者多年构建各种 Python 应用的经验,从几个小时完成的小型系统脚本,到许多开发人员历经数年编写的大型应用。

本书描述了开发人员使用 Python 的最佳实践。

本书包含了一些主题,这些主题并不关注语言本身,而是更多地关注如何利用相关的工具和技术。

换句话说,本书描述了高级 Python 开发人员每天的工作方式。

本书内容

第1章介绍了 Python 语言及其社区的现状。本章展示了 Python 不断变化的方式及原因,还解释了为什么这些事实对任何想要自称 Python 专家的人来说是很重要的。本章还介绍了最流行和最公认的 Python 工作方式——常用的生产力工具和现已成为标准的约定。

第2章深入介绍迭代器、生成器、描述符等内容。本章还包括关于 Python 习语和 CPython 类型内部实现的有用注释,这些类型的计算复杂度是对这些习语的阐释。

第3章介绍了语法最佳实践,但重点放在类级别以上。本章包括 Python 中更高级的面向对象的概念和机制。学习这些知识是为了理解本章最后一节的内容,其中介绍的是 Python 元编程的各种方法。

第4章介绍了如何选择好的名称。它是对 PEP 8 中命名最佳实践的扩展,并且给出了一些如何设计良好 API 的提示。

第5章介绍如何创建 Python 包以及使用哪些工具,以便在官方的 Python 包索引或其他包仓库中正确地分发。对于 Python 包还补充了一些工具的简要回顾,这些工具可以让你用 Python 源代码创建独立可执行文件。

第6章主要针对 Python Web 开发人员和后端工程师,因为讲的是代码部署。本章解释了如何构建 Python 应用,使其可以轻松部署到远程服务器,还介绍了可以将这个过程自动化的工具。本章是第5章的延续,因此还介绍了如何使用包和私有包仓库来简化应用部署。

第7章解释了为什么为 Python 编写 C 扩展程序有时可能是一个好的解决方案。本章还展示了只要使用了正确的工具,它并不像想象中那么难。

第8章深入介绍了项目代码库的管理方式,还介绍了如何设置各种持续开发流程。

第9章包含文档相关的内容,提供了有关技术写作和 Python 项目文档化方式的建议。

第10章解释了测试驱动开发的基本原理,还介绍了可用于这种开发方法的工具。

第11章解释了何为优化,介绍了分析技术和优化策略指南。

第12章是对第11章的扩展,为 Python 程序中经常出现的性能问题提供了一些常用的解决方案。

第13章介绍了 Python 并发这一宏大的主题。本章解释了并发的概念、何时需要编写并发应用,以及 Python 程序员主要使用的并发方法。

第14章用一套有用的设计模式以及 Python 的代码示例对本书进行了总结。

阅读本书的前提

本书面向的是可以在任何操作系统上使用 Python 3 进行软件开发的人员。

这不是一本面向初学者的书,所以我假设你已经在开发环境中安装了 Python,或者知道如何安装 Python。不管怎样,本书考虑到以下事实:不是每个人都需要充分了解 Python 的最新功能或官方推荐的工具。因此,第1章概述了常见的实用程序(例如虚拟环境和 pip),这些实用程序现在已经成为 Python 专业开发人员的标准工具。

目标读者

本书面向的是想要进一步掌握 Python 的开发人员。开发人员主要指的是专业人士,即用 Python 编写软件的程序员。这是因为本书主要侧重于工具和实践,它们对于创建高性能的、可靠且可维护的 Python 软件至关重要。

这并不意味着业余爱好者无法从本书中发现有趣的内容。对于任何对学习 Python 高级概念感兴趣的人来说,本书都是很棒的。任何具备 Python 基本技能的人都应该能够读懂本书的内容,虽然经验不足的程序员可能需要一些额外的努力。对于有点落后仍在继续使用 Python 2.7 或更老版本的人来说,本书也是对 Python 3.5 的全面介绍。

最后,从阅读本书中受益最多的人群应该是 Web 开发者和后端工程师。这是因为本书重点介绍了在他们的工作领域中特别重要的两个主题:可靠的代码部署与并发。

本书约定

本书用多种文本样式来区分不同种类的信息。下面是这些样式的示例及其含义解释。

文本中的代码、数据库表的名称、文件夹名称、文件名称、文件扩展名、路径名称、虚拟 URL、用户输入和 Twitter 句柄的格式如下所示:“利用str.encode(encoding, errors)方法,用注册编解码器对字符串进行编码。”

代码块的格式如下所示:

[print("hello world")print "goodbye python2"

如果我们想让你将注意力集中在代码块的特定区域,相关的几行或几项将会被设成粗体,如下所示:

cdef long long fibonacci_cc(unsigned int n) nogil:    if n < 2:        return n    else:        return fibonacci_cc(n - 1) + fibonacci_cc(n - 2)

命令行的输入或输出如下所示:

$ pip show pip---Metadata-Version: 2.0Name: pipVersion: 7.1.2Summary: The PyPA recommended tool for installing Python packages.Home-page: https://pip.pypa.io/Author: The pip developersAuthor-email: python-virtualenv@groups.google.comLicense: MITLocation: /usr/lib/python2.7/site-packagesRequires:

新术语重要词语将以粗体显示。你会在屏幕上看到的单词(例如在菜单或对话框中)将以下面这种文本形式出现:“单击 Next 按钮可跳转至下一屏”。

读者反馈

我们十分欢迎读者的反馈意见。让我们了解你对本书的看法——喜欢哪些内容,不喜欢哪些内容。这些反馈对我们很重要,因为它有助于我们编写出对读者真正有帮助的书。

一般性的反馈请发送邮件至 feedback@packtpub.com,并在邮件主题中注明本书的标题。

如果你是某个领域的专家,并且有兴趣写一本书或者参与出版一本书,请参阅我们的作者指南。

客户支持

现在你已经成为这本 Packt 图书的拥有者,为了让你的购买物超所值,我们还为你提供了许多其他方面的服务。

下载示例代码

你可以用自己的账号在 Packt 的官方网站下载本书的示例代码文件。如果你是在其他地方购买的本书,可以访问 Packt 的官方网站并注册,文件会直接通过邮件发送给你。

下载代码文件的步骤如下所示。

  • 用你的电子邮件地址和密码登录或注册我们的网站。
  • 将鼠标指针悬停在顶部的 SUPPORT 选项卡上。
  • 单击 Code Downloads & Errata
  • Search 框中输入本书的名字。
  • 选择你要下载代码文件的书籍。
  • 从下拉菜单中选择本书的购买途径。
  • 单击 Code Download

你还可以在Packt网站的本书页面单击 Code Files 按钮来下载代码文件。在 Search 框输入本书的书名即可访问该页面。请注意,你需要登录 Packt 账号。

文件下载完成后,请确保用下列软件的最新版本对文件夹进行解压或提取。

  • 在 Windows 上用 WinRAR 或 7-Zip。
  • 在 Mac 上用 Zipeg、iZip 或 UnRarX。
  • 在 Linux 上用 7-Zip 或 PeaZip。

本书的代码包也托管在 GitHub。在 GitHub 上还有大量图书和视频资源。快去看一下吧!

勘误

尽管我们已经竭尽全力确保本书内容的准确性,但错误在所难免。如果你发现了书中的错误,无论是正文错误还是代码错误,希望你能将其报告给我们,我们将不胜感激。这样不仅能够减少其他读者的困惑,还能帮助我们改进本书后续版本的质量。如果你需要提交勘误,请访问 这里,选择相应的书名,单击 Errata Submission Form 链接,然后输入你的勘误信息并提交。一旦通过验证,我们将接受你提交的勘误,同时勘误内容也将被上传到我们的网站,或者被添加到对应勘误区的现有勘误列表中。

想要查看之前提交的勘误,请访问 这里,并在搜索框中输入相应的书名。你想查看的信息将出现在 Errata 下面。

侵权行为

所有媒体在互联网上都一直饱受版权侵害的困扰。Packt 坚持对版权和授权进行全力保护。如果你在互联网上发现我社图书任何形式的盗版,请立即为我们提供网址或网站名称,以便我们采取进一步的措施。

请将疑似盗版材料的链接发送到 copyright@packtpub.com。

我们感谢你对作者的保护,这有助于我们继续为你提供更有价值的内容。

疑难解答

如果你对本书的某个方面抱有疑问,请通过 questions@packtpub.com 联系我们,我们会尽力为你解决。

第01章:Python 现状(上)

Python 很适合开发者使用。

无论你或你的客户用的是什么操作系统,都可以使用 Python。例如你可以在 Linux 上工作,然后部署到其他系统上,除非你的代码与特定平台相关,或者用到了特定平台的库。但这一特性已经不新鲜了(Ruby、Java 等很多其他语言都可以做到这一点)。本书还会讲到 Python 的其他特性,所有这些特性是使得 Python 成为一家公司主力开发语言的重要原因。

本书主要讲的是 Python 的 3.5 版本,如果没有明确说明的话,书中所有代码示例都是用这个版本的 Python 编写的。由于这一版本尚未被广泛使用,本章将会向读者介绍一下 Python 3 的当前现状,同时介绍 Python 的现代开发方法。本章主要包括以下内容。

  • 如何保持 Python 2 和 Python 3 之间的兼容性。
  • 为了开发的顺利进行,在应用层面和操作系统层面如何解决开发环境隔离的问题。
  • 如何增强 Python 提示符的功能。
  • 如何使用 pip 安装 Python 包。

每本书的开头都要来点开胃小菜。如果你对 Python 已经很熟悉了(特别是最新的 3.x 版本),并且掌握了开发中做环境隔离的正确方法,你可以跳过本章的前两节,快速阅读其他小节即可。其他小节会讲到一些工具和资源,它们并非必不可少,但可以大大提高 Python 开发效率。不过一定要读一下关于应用层环境隔离和 pip 的一节,因为这一节提到的工具会在本书后面的内容中用到。

1.1 Python 的现状与未来

Python 的历史最早可追溯到20世纪80年代末,但是1.0版的发行时间是在1994年,所以 Python 并不是一门非常年轻的语言。这里本该介绍 Python 主要版本发布的整个时间线,但其实真正重要的日期只有一个:2008年12月3日,也就是 Python 3.0 的发布日期。

在写作本书时,Python 3 的首次发布已经过去了7年。PEP 404 也已经创建了4年,PEP 404 是“取消发布"(un-release)Python 2.8 并正式关闭 Python 2.x 分支的官方文档。虽然过去了这么长的时间,Python 社区中依然存在明显的分歧。语言本身在迅速发展,但大量用户却并不想更新版本。

1.2 Python 升级及其原因

原因很简单。Python 升级是因为有这样的需求。语言之间的竞争随时都在上演。每隔几个月都会突然冒出一门新语言,声称解决了之前所有语言中存在的问题。对于大多数类似的项目,开发人员很快就会失去兴趣,它们的名气也只是一时炒作。

不管怎样,这也表示存在着更严重的问题。人们之所以设计新的编程语言,是因为他们发现现有的语言无法以最佳方式来解决问题。认识不到这样的需求是目光短浅的。此外,Python 的使用范围也越来越广泛,人们发现它有许多可以改进的地方,也应该做出这样的改进。

Python 的很多改进往往是由特定应用领域的需求驱动的。其中最重要的领域是 Web 开发,这一领域需要 Python 改进对并发的处理。

有些变化只是由于 Python 项目的历史原因导致的。这些年已经发现了 Python 的一些不合理之处,有些是标准库模块结构混乱或冗余,有些是程序设计缺陷。最初,发布 Python 3 是要对这门语言进行较大的清理与更新,但结果显示,这个计划并没有收到预期的效果。在很长一段时间内,很多开发人员对 Python 3 只是抱着好奇的态度而已,但希望这种情形正在好转。

1.3 追踪 Python 最新变化——PEP 文档

Python 社区有一种应对变化的固定方法。虽然各种各样的 Python 语言修改意见主要在邮件列表(python-ideas@python.org)中进行讨论,但只有发布了名为 PEP 的新文档,新的变化才会生效。PEP 的全称是 Python 改进提案(Python Enhancement Proposal,PEP)。它是提交 Python 变化的书面文档,也是社区对这一变化进行讨论的出发点。这些文档的整个目的、格式和工作流程的标准格式也都包含在一份 Python 改进提案中,也就是 PEP 1 文档

PEP 文档对 Python 的作用十分重要,根据讨论的主题,PEP 主要有以下3种用途。

  • 通知:汇总 Python 核心开发者需要的信息,并通知 Python 发布日程。
  • 标准化:提供代码风格、文档或其他指导意见。
  • 设计:对提交的功能进行说明。

所有提交过的 PEP 都被汇总在一个文档中,就是 PEP 0。由于这些 PEP 都在同一个网站上很容易找到,其 URL 也很容易猜到,因此本书一般用编号来指代这些文档。

如果你对 Python 语言的未来发展方向感兴趣,但又没时间跟踪 Python 邮件列表中的讨论,那么 PEP 0 会是很好的信息来源。它会告诉你,哪些文档已被接受但尚未实施,哪些文档仍在审议中。

PEP还有其他的用途。人们通常会问这样的问题:

  • A 功能为什么要以这样的方式运行?
  • Python 为什么没有B功能?

大多数情况下,关于该功能的某个 PEP 文档已经给出了上述问题的详细回答。很多提交的关于 Python 语言功能的 PEP 文档并没有通过。这些文档可作为历史资料来参考。

1.4 当前 Python 3 的普及程度

Python 3 有许多强大的新功能,那么它在社区中广泛普及了吗?遗憾的是,并没有。有一个著名的网站叫“Python 3 荣耀之墙(Python 3 Wall of Superpowers)”,里面记录了大多数常用软件包与 Python 3 的兼容性,不久前这个网站刚刚改名为“Python 3 耻辱之墙(Python 3 Wall of Shame)”。目前这种状况正在逐步改善,上述网站的软件包列表中绿色的比例也在每月缓慢增加 [1]。尽管如此,但这并不代表很快所有应用开发团队都只使用 Python 3。当所有常用软件包都支持 Python 3 时,“我们所用的软件包还没有迁移到 Python 3”这一常用借口将不再适用。

造成目前这种状况的主要原因是,将现有应用从 Python 2 迁移到 Python 3 上总是一项不小的挑战。像2to3之类的工具可以进行代码自动转换,但无法保证转换后的代码100%正确。而且,如果不做人工修改的话,转换后的代码性能可能不如转换前。将现有的复杂代码库迁移到 Python 3 上可能需要付出巨大的精力和成本,某些公司可能无法负担这些成本。但这些成本可以分割成小份来逐步完成。一些优秀的软件架构设计方法可以帮助其逐步实现这一目标,如面向服务的架构或者微服务。新的项目组件(服务或微服务)可以用新方法编写,现有的项目组件可以逐步迁移。

长远来看,将项目迁移到 Python 3 只有好处。根据 PEP-404 这份文档,Python 2.x 分支将不会发布2.8版本。而且未来所有重要的项目(如 Django、Flask 和 NumPy)可能都将放弃 2.x 的兼容性,仅支持 Python 3。

我个人对这个问题的观点可能会引发争议。我认为在创建新的软件包时,最好鼓励社区完全放弃支持 Python 2。当然,这一做法极大地限制了这些软件的适用范围,但对于那些坚持使用 Python 2.x 的人来说,这可能是改变他们想法的唯一方法。

1.5 Python 3 和 Python 2 的主要差异

前面已经说过,Python 3 打破了对 Python 2 的向后兼容。但它并不是完全重新设计的。而且,也并不是说 2.x 版本的 Python 模块在 Python 3 下都无法运行。代码可以完全跨版本兼容,无需其他工具或技术在两大版本上都可以运行,但一般只有简单应用才能做到这一点。

1.5.1 为什么要关注这些差异

本章前面说过我个人对 Python 2 兼容性的看法,但是目前不可能完全忽视这一点。还有一些 Python 包(例如第6章将讲到的fabric)十分实用,但可能短期内不会迁移到 Python 3。

另外,有时我们还会受到所在公司的制约。现有的遗留代码可能非常复杂,迁移代码的费用难以承受。所以即使我们现在决定只用 Python 3,短期内也不可能完全放弃 Python 2。

如今想要自称专业开发者,没有对社区的回馈是说不过去的,所以帮助开源软件开发者向现有软件包中添加对 Python 3 的兼容,可以很好地偿还在使用这些软件包时产生的“道德债(moral debt)”。当然,不了解 Python 2 和 Python 3 的差异是无法做到这一点的。顺便提一下,对于 Python 3 新手来说,这也是一项很好的练习。

1.5.2 主要的语法差异和常见陷阱

要比较不同版本之间的差异,最好的参考资料就是 Python 文档。不过为了方便读者,本节总结了其中最重要的内容。但不熟悉 Python 3 的读者还是要去阅读官方文档。

Python 3 引入的重要差异一般可分为以下几个方面。

  • 语法变化,删除/修改了一些语法元素,并添加了一些新的语法元素。
  • 标准库中的变化。
  • 数据类型与集合的变化。
1.语法变化

有些语法变化会导致当前代码无法运行,这些变化是最容易发现的,它们会导致代码根本无法运行。包含新语法元素的 Python 3 代码在 Python 2 中无法运行,反之亦然。由于删除了某些元素,导致 Python 2 代码显然无法与 Python 3 兼容。运行有这些问题的代码时,解释器很快就会抛出SyntaxError异常。下面是一个无法运行的脚本示例,只包含两个语句,都会引发语法错误而无法运行:

   print("hello world")   print "goodbye python2"

上述代码在 Python 3 中的实际运行结果如下:

$ python3 script.py  File "script.py", line 2    print "goodbye python2"                         ^SyntaxError: Missing parentheses in call to 'print'

列出所有的语法差异会比较长,而且 Python 3.x 的新版本也会不时添加新的语法元素,在较早版本的 Python 中就会引发错误(即使在相同的3.x版本上也会报错)。其中最重要的语法差异将会在第2章和第3章中讲到,所以这里无需全部列出。

与 Python 2.7 相比,删除或改动的内容要相对少一些,下面给出最重要的变化内容。

  • print不再是一条语句而是一个函数,所以必须加上括号。
  • 捕获异常的语法由except exc, var改为except exc as var
  • 弃用比较运算符<>,改用!=
  • from module import *(https://docs.python.org/3.0/reference/simple_stmts.html#import)现在只能用于模块,不能用在函数中。
  • 现在from .[module] import name是相对导入的唯一正确的语法。所有不以点字符开头的导入都被当作绝对导入。
  • sorted函数与列表的sort方法不再接受cmp参数,应该用key参数来代替。
  • 整数除法表达式(如1/2)返回的是浮点数。取整运算可以用//运算符,如1//2。这样做的好处是浮点数也可以用这个运算符,所以5.0//2.0 == 2.0
2.标准库中的变化

语法变化很容易发现,标准库中的重大变化也是非常容易发现的。Python 的每个后续版本都会向标准库模块中添加、弃用、改进或完全删除某些内容。在旧版 Python(1.x和2.x)中也会定期有这样的变化,所以出现在 Python 3 中并不让人吃惊。大多数情况下,对于删除或重组的模块(例如urlparse移到了urllib.parse),在运行解释器时会对导入语句抛出异常。这样的问题很容易发现。无论如何,为了确保能够发现所有类似的问题,完整的代码测试覆盖率是必不可少的。在某些情况下(例如使用延迟加载模块时),这个通常在全局导入时出现的问题并不会出现,直到在代码中将某些模块作为函数调用时才会出现。因此,在测试期间确保每行代码都要实际运行是很重要的。

延迟加载模块

延迟加载模块是指在全局导入时并不加载的模块。在 Python中,import语句可以包含在函数内部,这样导入是在函数调用时才会发生,而不是在全局导入时发生。在某些情况下,模块的这种加载方式可能比较合理,但大多数情况下,这只是对设计不佳的模块结构的变通方法(例如避免循环导入),通常应避免这种加载方式。当然,对于标准库模块来说,没有理由使用延迟加载。

3.数据类型与集合的变化

开发人员在努力保持兼容性或只是将现有代码迁移到 Python 3 上时,需要特别注意 Python 中数据类型与集合的表示方式的变化。虽然不兼容的语法变化或标准库变化很容易发现,也很容易修复,但集合与数据类型的变化要么难以察觉,要么需要大量的重复工作。这样的变化列表会很长,再次重申,官方文档是最好的参考资料。

不过,这一节必须讲一下 Python 3 中字符串处理方式的变化,因为这是 Python 3 中最具争议也是讨论最多的变化,尽管这是一件好事,使很多问题变得更加明确。

现在所有字符串都是 Unicode,字节(bytes)需要加一个bB的前缀。Python 3.0 和 3.1 不支持使用u前缀(例如u"foo"),使用的话会引发语法错误。不支持这个前缀是引发所有争议的主要原因。这导致难以编写能够兼容 Python 不同分支的代码,2.x版需要用这个前缀来创建 Unicode。Python 3.3 又恢复了这个前缀,虽然没有任何语法上的意义,只是为了简化兼容过程。

1.5.3 用于保持跨版本兼容性的常用工具和技术

在 Python 不同版本之间保持兼容性是一项挑战。根据项目的大小不同,这项挑战可能会增加许多额外的工作量,但绝对可行,也很值得去做。对于在许多环境中都会用到的 Python 包来说,必须要保持跨版本兼容性。如果开源包没有定义明确并经过测试的兼容范围(compatibility bound),是不太可能流行起来的。而且,对于只在公司网络封闭使用的第三方代码来说,也可以大大受益于在不同环境中的测试。

这里应该注意,虽然这一部分内容主要关注 Python 不同版本之间的兼容,但这些方法也适用于保持与外部依赖项之间的兼容,外部依赖项包括不同的包版本、二进制库、系统或外部服务等。

整个过程主要分为3个部分,按重要性排序如下。

  • 定义并记录目标兼容范围的及其管理方法。
  • 在每个环境中进行测试,并对每个兼容的依赖版本进行测试。
  • 实现实际的兼容代码。

告知兼容范围是整个过程中最重要的一部分,因为这可以让代码使用者(开发人员)对代码的工作原理和未来的变化方式有一定的预期和假设。我们的代码可能用于多个不同项目的依赖,这些项目也在努力管理兼容性,所以把代码兼容性说清楚还是很重要的。

本书总是尽量给出几个选择,而不会强烈推荐某个特定选项,而这里是少数几个例外之一。目前来看,管理兼容性未来变化的最佳方法,就是正确使用语义化版本(Semantic Versioning semver)的版本号。它是一个广为接受的标准,用仅包含3个数字的版本标识符来标记代码的变化范围。它还给出了如何处理弃用的方法建议。下面是摘录 semver 官网的摘要。

版本格式:主版本号.次版本号.修订号,版本号递增规则如下。

  • 主版本号(MAJOR):当你做了不兼容的 API 修改。
  • 次版本号(MINOR):当你做了向后兼容的功能性新增。
  • 修订号(PATCH):当你做了向后兼容的问题修正。

先行版本号及版本编译信息可以加到“主版本号.次版本号.修订号”的后面,作为延伸。

测试时就会发现一个悲伤的事实,为了保证代码与每个依赖版本和每个环境(这里环境指的是 Python 版本)都保持兼容,必须在所有可能的组合中对代码进行测试。当然,如果项目的依赖很多,做到这一点基本是不可能的,因为随着依赖版本数目的增加,组合的数目也会迅速增加。因此,通常需要做一些权衡,使得运行所有兼容性测试无需花费数年的时间。第10章中介绍一般的测试,里面也介绍了所谓的矩阵测试中工具的选择。

项目遵循 semver 的好处在于,通常只有主版本才需要测试,因为次版本和修订版本中保证没有向后不兼容的变化。只有项目不违背这样的约定,这种说法才能成立。不幸的是,每个人都会犯错,许多项目中都出现了后向不兼容的变化,甚至在修订版本中也出现了这种变化。尽管如此,由于 semver 声称对次版本和修订版本的变化保持严格的向后兼容,那么打破这个规则就可以视为 bug,可以在修订版本中进行修复。

如果明确定义了兼容范围并严格测试,那么实现兼容层就是最后一步,也是最不重要的一步。但是,每一位对这个话题感兴趣的程序员都应该知道下列工具和技术。

最基本的就是 Python 的__future__模块。它将Python新版本中的一些功能反向迁移到旧版本中,采用的是导入语句的形式:

   from __future__ import <feature>

future语句提供的功能是和语法相关的元素,其他方法很难处理这些元素。这个语句只能影响它所在的模块。下面是 Python 2.7 交互式会话的实例,从 Python 3.0 中引入 Unicode:

Python 2.7.10 (default, May 23 2015, 09:40:32) [MSC v.1500 32 bit(Intel)] on win32Type "help", "copyright", "credits" or "license" for moreinformation.>>> type("foo")  # 旧的字面值<type 'str'>>>> from __future__ import unicode_literals>>> type("foo")  # 现在变成了unicode<type 'unicode'>

下面列出了所有可用的__future__语句,关注2/3兼容性的开发者都应该知道。

  • division:Python 3 新增的除法运算符(PEP 238)。
  • absolute_import:将所有不以点字符开头的import语句格式解释为绝对导入(PEP 328)。
  • print_function:将print语句变为函数调用,所以在print后面必须加括号(PEP 3112)。
  • unicode_literals:将每个字符串解释为Unicode(PEP 3112)。

__future__中的可选语句列表很短,只包含几个语法功能。对于其他变化的内容,例如元类语法(第3章会讲到这一高级特性),维持其兼容性则困难得多。future语句也无法完全解决多个标准库重组的问题。幸运的是,有些工具旨在提供一致可用的兼容层。最有名的就是 Six 模块,提供了常用的2/3兼容性的整个样板。另一个很有前途但名气稍逊的工具是 future 模块。

在某些情况下,开发人员可能不想在一些小型 Python 包里添加其他依赖项。通常的做法是将所有兼容性代码放在一个附加模块中,该模块通常命名为compat.py。下面是来自python-gmaps项目的compat模块实例:

   # -*- coding: utf-8 -*-   import sys   if sys.version_info < (3, 0, 0):       import urlparse  # noqa       def is_string(s):           return isinstance(s, basestring)   else:       from urllib import parse as urlparse  # noqa       def is_string(s):           return isinstance(s, str)

这样的compat.py模块十分常见,即使是利用 Six 保持2/3兼容性的项目也很常见,因为这种方法非常方便,用于保存在不同版本的依赖包之间保持兼容性的代码。

下载示例代码

你可以用自己的账号在 Packt 的官方网站下载本书的示例代码文件。如果你是在其他地方购买的本书,你可以访问 Packt 的官方网站并注册,文件会直接通过邮件发送给你。

下载代码文件的步骤如下。

  • 用你的电子邮件地址和密码登录或注册我们的网站。

  • 将鼠标指针悬停在顶部的 SUPPORT 选项卡上。

  • 单击 Code Downloads & Errata

  • Search 框中输入本书的名字。

  • 选择你要下载代码文件的书籍。

  • 从下拉菜单中选择本书的购买途径。

  • 单击 Code Download

文件下载完成后,请确保用下列软件的最新版本对文件夹进行解压或提取。

  • 在 Windows 上用 WinRAR 或 7-Zip。

  • 在 Mac 上用 Zipeg、iZip 或 UnRarX。

  • 在 Linux 上用 7-Zip 或 PeaZip。

本书的代码包也托管在 GitHub。在 GitHub 上还有大量图书和视频资源。去看一下吧!


[1] 在这个网站上,如果某个软件包被标为绿色,则表示它支持Python 3,红色则表示不支持。——译者注

第01章:Python 现状(中)

1.6 不只是 CPython

最重要的 Python 实现是用 C 语言编写的,叫作 CPython。大多数人在讨论 Python 时指的都是 CPython。随着语言的进化,C 语言实现也相应发生变化。除了 C 之外,Python 还有其他几种实现方式,这些实现方式都在努力地跟上主流。大多数实现方式的时间表都要落后于 CPython,但它们提供了一个好机会,可以在具体环境中使用并推广 Python 语言。

1.6.1 为什么要关注 Python 实现

Python 实现有许多种。在 Python 官网上关于这一话题的维基百科页面中,主要介绍了20多种语言变体、方言或除 C 语言之外的 Python 解释器实现。其中一些只是实现了语言核心语法、功能和内置扩展的一个子集,但至少有几个与 CPython 几乎完全兼容。最重要的是,虽然其中一些只是玩具项目或实验,但大部分都是为了解决某些实际问题而创建的,这些问题要么是用 CPython 无法解决,要么需要开发人员花费巨大的精力。这些问题的实例包括如下几个。

  • 在嵌入式系统中运行 Python 代码。
  • 与运行框架(如 Java 或 .NET)或其他语言做代码集成。
  • 在 Web 浏览器中运行 Python 代码。

本节将简要介绍目前 Python 开发人员可用的最流行和最新的 Python 实现。

1.6.2 Stackless Python

Stackless Python 自称 Python 增强版。之所以名为 Stackless(无栈),是因为它没有依赖 C 语言的调用栈。它实际上是修改过的 CPython 代码,还添加了一些新的功能,在创建 Stackless Python 时 Python 核心实现中还没有这些功能。其中最重要的功能就是由解释器管理的微线程,用来替代依赖系统内核上下文切换和任务调度的普通线程,既轻量化又节约资源。

Stackless Python 最新可用的版本是 2.7.9 和 3.3.5,分别实现的是 Python 2.7和3.3。在 Stackless Python 中,所有的额外功能都是内置stackless模块内的框架。

Stackless Python 并不是最有名的 Python 实现,但很值得一提,因为它引入的思想对编程语言社区有很大的影响。将 Stackless Python 中的内核切换功能提取出来并作为一个独立包发布,名为greenlet,现在是许多有用的库和框架的基础。此外,它的大部分功能都在 PyPy 中重新实现,PyPy 是另一个 Python 实现,我们将稍后介绍。

1.6.3 Jython

Jython 是 Python 语言的 Java 实现。它将代码编译为 Java 字节代码,开发人员在 Python 模块中可以无缝使用 Java 类。Jython 允许人们在复杂应用系统(例如 J2EE)中使用 Python 作为顶层脚本语言,它还将 Java 应用引入到 Python 世界中。Jython 的一个很好的例子就是,在 Python 程序中可以使用 Apache Jackrabbit(这是一个基于 JCR 的文档仓库 API)。

Jython 最新可用的版本是 Jython 2.7,对应的是 Python 2.7 版。它宣称几乎实现了 Python 所有的核心标准库,并使用相同的回归测试套件。Jython 3.x 版正在开发中。

Jython 与 CPython 实现的主要区别如下所示。

  • 真正的 Java 垃圾回收,而不是引用计数。
  • 没有全局解释器锁(Global Interpreter Lock,GIL),在多线程应用中可以充分利用多个内核。

这一语言实现的主要缺点是缺少对 C/Python 扩展 API 的支持,因此用 C 语言编写的 Python 扩展在 Jython 中无法运行。这种情况未来可能会发生改变,因为 Jython 3.x 计划支持 C/Python 扩展 API。

某些 Python Web 框架(例如 Pylons)被认为是促进 Jython 的开发,使其可用于 Java 世界。

1.6.4 IronPython

IronPython 将 Python 引入 .NET 框架中。这个项目受到微软的支持,IronPython 的主要开发人员都在微软工作。它是推广语言的一种重要实现。除了 Java,.NET 社区是最大的开发者社区之一。还值得注意的是,微软提供了一套免费开发工具,可以将 Visual Studio 转换为成熟的 Python IDE。这是作为 Visual Studio 的插件发布的,名为 PTVS(Python Tools for Visual Studio,用于 Visual Studio 的 Python 工具),在 GitHub 可以找到其开源代码。

最新的稳定版本是2.7.5,与 Python 2.7 兼容。与 Jython 类似,Python 3.x 的实现也在开发中,但还没有可用的稳定版本。虽然 .NET 主要在微软 Windows 系统上运行,但是 IronPython 也可以在 Mac OS X 和 Linux 系统上运行。这一点要感谢 Mono,一个跨平台的开源 .NET 实现。

与 CPython 相比,IronPython 的主要区别或优点如下。

  • 与 Jython 类似,没有全局解释器锁(Global Interpreter Lock,GIL),在多线程应用中可以充分利用多个内核。
  • 用 C# 和其他 .NET 语言编写的代码可以轻松集成到 IronPython 中,反之亦然。
  • 通过 Silverlight,在所有主流 Web 浏览器中都可以运行。

说到弱点,IronPython 也与 Jython 非常类似,因为它也不支持 C/Python 扩展 API。对于想要使用主要基于 C 扩展的 Python 包(例如 NumPy)的开发人员来说,这一点是很重要的。有一个叫作 ironclad 的项目,其目的是在 IronPython 中无缝使用这些扩展,虽然它最新支持的版本是2.6,开发似乎也停止了。

1.6.5 PyPy

PyPy 可能是最令人兴奋的 Python 实现,因为其目标就是将 Python 重写为 Python。在 PyPy 中,Python 解释器本身是用 Python 编写的。在 Python 的 CPython 实现中,有一个 C 代码层来实现具体细节。但在 PyPy 实现中,这个 C 代码层用 Python 完全重写。

这样你可以在代码运行期间改变解释器的行为,并实现 CPython 难以实现的代码模式。

目前 PyPy 的目的是与 Python 2.7 完全兼容,而 PyPy3 则与 Python 3.2.5 版兼容。

以前对 PyPy 感兴趣主要是理论上的原因,只有喜欢深入钻研语言细节的人才会对它感兴趣。它通常不用于生产环境,但这些年来这种状况已经发生改变。现在许多基准测试给出惊人的结果,PyPy 通常比 CPython 实现要快得多。这个项目有自己的基准测试网站,记录了用数十种不同的基准测试对每一版本性能的测量结果,详见这里。网站清晰地显示,启用 JIT 的 PyPy 至少比 CPython 要快好几倍。由于 PyPy 的这一特性以及其他特性,使得越来越多的开发人员决定在生产环境中切换到 PyPy。

PyPy 与 CPython 实现的主要区别在于以下几个方面。

  • 使用垃圾回收,而不是引用计数。
  • 集成跟踪 JIT 编译器,可以显著提高性能。
  • 借鉴了 Stackless Python 在应用层的无栈特性。

与几乎所有其他的 Python 实现类似,PyPy 也缺乏对 C/Python 扩展 API 的完全官方支持。但它至少通过 CPyExt 子系统为 C 扩展提供了某种程度的支持,虽然文档不完整,功能也尚未完善。此外,社区正在努力将 NumPy 迁移到 PyPy 中,因为这是最需要的功能。

1.7 Python 开发的现代方法

作为专家,最重要的是要对所选用的编程语言有深刻的理解。对于任何技术来说都是如此。但如果不知道在特定语言社区中的常用工具和实践的话,想开发一款好软件是相当困难的。Python 所有的单项功能都可以在其他某种语言中找到。所以,直接比较语法、表现力(expressiveness)或性能的话,总会在一个或多个方面存在更好的解决方案。但 Python 真正出众的领域在于围绕语言打造的整个生态系统。多年来,Python 社区完善了标准实践和标准库,有助于在更短的时间内创建更可靠的软件。

对于上文提到的生态系统,最明显也最重要的一部分就是大量免费的开源包,可以用来解决许多问题。编写新软件总是一个费钱又费时的过程。能够复用现有代码而无需重新造轮子(reinvent the wheel),可以大大降低开发的时间和成本。这也是某些公司的项目在经济上可行的唯一原因。

由于这个原因,Python 开发者花费大量精力来创建工具和标准,方便使用他人创建的开源包。我们首先介绍虚拟隔离环境、改进的交互式 shell 和调试器,然后介绍一些程序,有助于发现、搜索和分析 PyPIPython Package Index,Python 包索引)上大量可用的 Python 包。

1.8 应用层 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.0Name: pipVersion: 7.1.2Summary: The PyPA recommended tool for installing Python packages.Home-page: https://pip.pypa.io/Author: The pip developersAuthor-email: python-virtualenv@groups.google.comLicense: MITLocation: /usr/lib/python2.7/site-packagesRequires:$ 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/sInstalling collected packages: pip  Found existing installation: pip 7.1.2    Uninstalling pip-7.1.2:      Successfully uninstalled pip-7.1.2Successfully installed pip-6.1.1You 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 pipYou 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.whlInstalling collected packages: pip  Found existing installation: pip 6.1.1    Uninstalling pip-6.1.1:      Successfully uninstalled pip-6.1.1Successfully installed pip-7.1.2

在某些情况下,可能默认pip不可用。从 Python 3.4 版和 2.7.9 版开始,总是可以使用ensurepip模块来引导启动pip,具体如下:

$ python -m ensurepipIgnoring indexes: https://pypi.python.org/simpleRequirement already satisfied (use --upgrade to upgrade): setuptools in /usr/lib/python2.7/site-packagesCollecting pipInstalling collected packages: pipSuccessfully installed pip-6.1.1

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

1.8.1 为什么要隔离

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 包。其设置非常简单,通常也足以保证小项目和小软件包开发过程中的隔离。

不幸的是,在某些情况下,这种做法可能不足以保证充分的一致性和可重复性。对于这种情况,系统级隔离是对工作流程很好的补充,本章后面也会介绍一些可用的方案。

1.8.2 常用解决方案

在运行时隔离 Python 的方法有几种。最简单也最显而易见的方法,就是手动修改PATHPYTHONPATH环境变量或将 Python 二进制文件移动到其他位置,以改变它发现可用 Python 包的方式,将环境变量修改成保存项目依赖的自定义位置,当然这种方法也最难维护。幸运的是,有几种工具可以帮助维护虚拟环境,并维护系统中安装包的存储方式。这些工具主要包括:virtualenvvenvbuildout。它们在底层做的事情实际上与我们手动做的一样。实际的策略取决于具体的工具实现。但一般来说,它们更方便使用,而且还有其他好处。

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 exampleNew python executable in example/bin/pythonInstalling setuptools, pip, wheel...done.$ source example/bin/activate(example)$ deactivate$

关于virtualenv要注意,最重要的是它完全依赖于在文件系统中的存储状态。它不会提供额外功能来跟踪应该安装哪些包。这些虚拟环境不可移植,不能移动到其他系统或机器。对每个新的应用部署来说,都需要从头开始创建新的虚拟环境。因此,virtualenv用户有一个良好实践,就是将所有项目依赖保存到一个requirements.txt文件(约定命名)中,正如下面的代码所示:

   # 井号(#)后面的内容是注释。   # 明确版本号,可重复性高。   eventlet==0.17.4   graceful==0.1.1   # 如果项目在不同依赖版本中都通过测试,   # 也可以指定相对版本编号。   falcon>=0.3.0,<0.5.0   # 应尽量明确Python包的版本,   # 除非始终需要最新版。   pytz

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

pip install -r requirements.txt

需要记住,需求文件并不总是理想的解决方案,因为它没有给定依赖的准确列表,而只给出了需要安装的依赖。因此,如果需求文件并非最新版,无法反映环境的实际状态,那么整个项目在开发环境中可以正常运行,但在其他环境中却无法启动。当然,pip freeze命令可以打印出当前环境所有的 Python 包,但不应该盲目使用这个命令。它会打印出所有内容,甚至那些仅用于测试而并不用于项目的 Python 包。本书提到的另一款工具buildout就解决了这个问题,所以可能是某些开发团队的更佳选择。

对于 Windows 用户来说,Windows 下的virtualenv对内部目录结构使用了一种不同的命名方式。你要用Scripts/Libs/Include/3个目录,而不是bin/lib/include/,以更好地匹配这种操作系统上的开发约定。用于激活/关闭环境的命令也不一样。你要用ENV/Scripts/activate.batENV/Scripts/deactivate.bat,而不是将source命令作用在activatedeactivate脚本上。

2.venv

虚拟环境很快逐步完善,成为了社区中的常用工具。从 Python 3.3 开始,标准库已经支持创建虚拟环境。其用法与 Virtualenv 几乎相同,虽然命令行选项采用了不同的命名约定。新的venv模块提供了pyvenv脚本,可以用于创建新的虚拟环境:

pyvenv ENV

这里的ENV应替换为新环境的名字。此外,现在也可以用 Python 代码直接创建新的环境,因为所有功能都包含在内置的venv模块中。其他用法和实现细节(例如环境目录的结构、激活/关闭脚本)与 Virtualenv 几乎完全相同,所以换用这种方法应该很简单,也不会牵扯太多精力。

对于使用较新版本 Python 的开发人员来说,推荐使用venv而不是 Virtualenv。对于 Python 3.3 版,切换到venv可能需要付出更多的精力,因为这一版本在新环境中没有默认安装setuptoolspip,所以用户需要手动安装它们。幸运的是,这一点在 Python 3.4 中已经修改,并且由于venv的可定制性,其内容可以被改写。对于细节的解释可参见 Python 文档,但有些用户可能会认为它过于复杂,仍然在这一版本的 Python 中继续使用 Virtualenv。

3.buildout

buildout 是一个强大工具,可与引导启动并部署用 Python 编写的应用。它的一些高级特性将在本书后面讲到。在很长一段时间内,他还被用作创建 Python 隔离环境的工具。由于 buildout 需要声明性的配置,每次依赖发生变化都必须修改配置,因此这些环境更容易复制和管理,无需依赖环境状态。

不幸的是,这一情况已发生变化。从2.0.0版开始,buildout包不再提供与系统 Python 在任何层级的隔离。处理隔离的任务留给其他工具来做,如 virtualenv,所以仍然可以用 buildout 来做隔离,但事情变得有点复杂。buildout 必须要在隔离环境中初始化才能真正隔离。

与之前版本的 buildout 相比,这一版本有一个主要缺点,就是它要依赖其他隔离方法。开发这些代码的开发人员不再确定对依赖的描述是否完整,因为有些 Python 包可以绕过声明性配置来安装。当然,这个问题可以通过适当的测试和发布过程来解决,但却使整个工作流程更加复杂。

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

1.8.3 选择哪种工具

不存在适用于所有情况的最佳解决方案。一家公司认为好的解决方案可能并不适用于其他团队的工作流程。而且每个应用的需求也各不相同。小项目可以只使用virtualenvvenv,比较简单,但大型项目可能还需要buildout的帮助,以便进行更复杂的装配。

之前没有详细说明的是,在 buildout 早期版本(2.0.0版之前)中,可以在隔离环境中对项目进行装配,其结果与 Virtualenv 给出的结果类似。不幸的是,这个项目的1.x分支不再受到维护,所以不建议因为这个原因使用它。

我推荐尽可能使用venv模块,而不是 virtualenv。因此,对于面向 Python 3.4 或更高版本的项目,应该默认选择venv。在 Python 3.3 中使用venv可能不太方便,因为没有内置setuptoolspip的支持。对于面向更多 Python 版本(包括其他解释器和2.x分支)的项目,virtualenv 似乎是最佳选择。

第01章:Python 现状(下)
第02章:语法最佳实践——类级别以下(上)
第02章:语法最佳实践——类级别以下(中)
第02章:语法最佳实践——类级别以下(下)
第03章:语法最佳实践——类级别以上(上)
第03章:语法最佳实践——类级别以上(中)
第03章:语法最佳实践——类级别以上(下)
第04章:选择好的名称(上)
第04章:选择好的名称(中)
第04章:选择好的名称(下)
第05章:编写一个包(上)
第05章:编写一个包(中)
第05章:编写一个包(下)
第06章:部署代码(上)
第06章:部署代码(中)
第06章:部署代码(下)
第07章:使用其他语言开发 Python 扩展(上)
第07章:使用其他语言开发 Python 扩展(中)
第07章:使用其他语言开发 Python 扩展(下)
第08章:管理代码(上)
第08章:管理代码(中)
第08章:管理代码(下)
第09章:文档化你的项目(上)
第09章:文档化你的项目(中)
第09章:文档化你的项目(下)
第10章:测试驱动开发(上)
第10章:测试驱动开发(中)
第10章:测试驱动开发(下)
第11章:优化——一般原则与分析技术(上)
第11章:优化——一般原则与分析技术(中)
第11章:优化——一般原则与分析技术(下)
第12章:优化——一些强大的技术(上)
第12章:优化——一些强大的技术(中)
第12章:优化——一些强大的技术(下)
第13章:并发(上)
第13章:并发(中)
第13章:并发(下)
第14章:有用的设计模式(上)
第14章:有用的设计模式(中)
第14章:有用的设计模式(下)

阅读全文: http://gitbook.cn/gitchat/geekbook/5a389426c5896e6e1cf14e25

评论 2
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值