Python 对开发人员很有好处。
无论您或您的客户正在运行什么操作系统,它都会工作。除非您正在编写特定于平台的代码,或者使用特定于平台的库,否则您可以在 Linux 上工作并在其他系统上部署,例如。然而,这已经不再少见了(Ruby、Java 和许多其他语言都以同样的方式工作)。结合我们将在本书中发现的其他特性,Python 成为公司主要开发语言的明智选择。
本书重点介绍 Python 的最新版本 3.5,除非明确提到另一个版本,否则所有代码示例都是用该语言的这个版本编写的。由于此版本尚未广泛使用,因此本章包含一些对 Python 3 当前现状的描述,以向读者介绍 Python 3,以及一些关于 Python 开发的现代方法的介绍性信息。本章涵盖以下主题:
- 如何保持 Python 2 和 Python 3 之间的兼容性
- 为了开发的目的,如何在应用程序和操作系统级别处理环境隔离问题
- 如何增强 Python 提示符
- 如何使用 pip 安装软件包
一本书总是从一些开胃菜开始。因此,如果您已经熟悉 Python(特别是最新的 3.x 分支),并且知道如何为开发目的正确隔离环境,那么您可以跳过本章的前两部分,快速阅读其他部分。它们描述了一些工具和资源,这些工具和资源不是必不可少的,但可以极大地提高 Python 的生产率。不过,请务必阅读有关应用程序级环境隔离和 pip 的部分,因为在本书的其余部分中,它们的安装是强制性的。
Python 的历史始于 20 世纪 80 年代末,但它的 1.0 发布日期是 1994 年,因此它不是一种非常年轻的语言。这里提到的主要 Python 版本可能有一个完整的时间表,但真正重要的是一个日期:2008 年 12 月 3 日——Python 3.0 的发布日期。
在撰写本文时,自第一次发布 Python 3 以来已经过去了七年。自创建 PEP404(官方文件)“联合发布”Python2.8 并正式关闭 2.x 分支机构)以来,这也是四年前的事了。虽然已经过去了很多时间,但是 Python 社区中存在着一种特殊的二分法,尽管该语言发展非常快,但有一大群用户不想继续使用它。
答案是简单的 Python 更改,因为有这样的需要。比赛不睡觉。每隔几个月,就有一种新的语言不知从何处冒出来,声称能解决其所有前身的问题。大多数这样的项目很快就失去了开发人员的注意力,它们的受欢迎程度是由突然的炒作推动的。
无论如何,这是一个更大问题的迹象。人们设计新的语言是因为他们发现现有的语言不适合以最好的方式解决他们的问题。不承认这种需要是愚蠢的。而且,Python 越来越广泛的使用表明,它可以而且应该在许多地方得到改进。
Python 中的许多改进通常是由使用它的特定领域的需求驱动的。最重要的是 web 开发,这需要改进 Python 中的并发性。
有些变化是由 Python 项目的年龄和成熟度引起的。这些年来,它收集了一些杂乱无章的东西,比如去组织和冗余的标准库模块,或者一些糟糕的设计决策。首先,Python3 的发布旨在对语言进行重大清理和更新,但时间表明,这一计划有点事与愿违。很长一段时间以来,许多开发人员只把它当作好奇心来对待,但希望这种情况正在改变。
Python 社区有一种成熟的处理变化的方法。虽然推测性的 Python 语言思想主要在特定的邮件列表(<[python-ideas@python.org](mailto:python-ideas@python.org)>上)上讨论,但是如果没有一个称为 PEP 的新文档的存在,任何主要内容都不会改变。PEP是Python 增强方案。这是一篇提出对 Python 进行更改的论文,也是社区讨论的起点。围绕这些文档的整个目的、格式和工作流程也以 Python 增强方案的形式进行了标准化,即 PEP 1 文档(http://www.python.org/dev/peps/pep-0001 。
PEP 文档对于 Python 非常重要,根据主题的不同,它们有不同的用途:
- 通知:他们总结核心 Python 开发人员需要的信息,并通知 Python 发布时间表
- 标准化:它们提供代码样式、文档或其他指南
- 设计:它们描述了提议的特征
所有拟定政治公众人物的列表见文件 PEP 0(https://www.python.org/dev/peps/ )。由于它们很容易在一个地方访问,而且实际的 URL 也很容易猜测,所以它们通常是通过书中的数字来引用的。
那些想知道 Python 语言的发展方向但没有时间跟踪 Python 邮件列表讨论的人,PEP 0 文档可能是一个很好的信息来源。它显示了哪些文件已被接受但尚未执行,哪些文件仍在审议中。
政治公众人物也有其他用途。人们经常会问这样的问题:
- 为什么一部作品是这样的?
- 为什么 Python 没有特性 B?
在大多数情况下,广泛的答案可在特定的政治公众人物文件中找到,其中已经提到了此类功能。有很多描述 Python 语言特性的 PEP 文档被提出,但没有被接受。这些文件留作历史参考。
那么,多亏了新的令人兴奋的特性,Python3 在其社区中得到了很好的采用吗?遗憾的是,还没有。热门页面 Python 3 超级大国之墙(https://python3wos.appspot.com )跟踪大多数流行软件包与 Python3 分支的兼容性的方法,直到不久前才被命名为 Python3 羞愧之墙。这种情况正在发生变化,所提到的页面上列出的包表每个月都在慢慢变“更绿”。不过,这并不意味着所有构建应用程序的团队不久将只使用 Python3。当 Python3 上所有流行的包都可用时,我们使用的流行借口(包未被移植)将不再有效。
出现这种情况的主要原因是,将现有应用程序从 Python 2 移植到 Python 3 始终是一项挑战。有像 2to3 这样的工具可以执行自动代码翻译,但它们不能确保结果 100%正确。此外,如果不进行手动调整,此类翻译代码的性能可能不如其原始形式。将现有的复杂代码库迁移到 Python3 可能需要付出巨大的努力和成本,而有些组织可能负担不起。不过,这些成本可以及时分摊。一些好的软件体系结构设计方法,如面向服务的体系结构或微服务,可以帮助逐步实现这一目标。新的项目组件(服务或微服务)可以使用新技术编写,现有组件可以一次移植一个。
从长远来看,迁移到 Python 3 只能对项目产生有益的影响。根据 PEP-404,Python 的 2.x 分支将不再有 2.8 版本。此外,将来可能会有一段时间,所有主要项目(如 Django、Flask 和 numpy)都会放弃任何 2.x 兼容性,并且只在 Python3 上可用。
我个人对这个话题的看法可能会引起争议。我认为社区的最佳激励是在创建新包时完全放弃对 Python 2 的支持。当然,这大大限制了此类软件的应用范围,但这可能是改变坚持使用 Python2.x 的人思维方式的唯一途径。
已经说过,Python3 破坏了与 Python2 的向后兼容性。不过,这并不是一个完全的重新设计。此外,这并不意味着为 2.x 版本编写的每个 Python 模块都将在 Python3 下停止工作。可以编写在两个主要版本上运行的完全交叉兼容的代码,而无需额外的工具或技术,但通常只适用于简单的应用程序。
尽管我个人对 Python 2 兼容性的看法在本章前面已经公开,但现在不可能简单地忘记它。仍然有一些有用的包(如 fabric,在第 6 章中提到,部署代码)确实值得使用,但不太可能在不久的将来进行移植。
此外,有时我们可能会受到所在组织的约束。现有的遗留代码可能非常复杂,移植它在经济上是不可行的。因此,即使我们决定从现在开始只生活在 Python3 世界中,在一段时间内完全没有 Python2 也是不可能的。
如今,如果不回馈社区,就很难将自己命名为专业开发人员,因此帮助开源开发人员在现有软件包中添加 Python 3 兼容性是一个很好的方式来偿还使用这些软件包所产生的“道德债务”。当然,如果不了解 Python2 和 Python3 之间的差异,就无法做到这一点。顺便说一句,对于那些新加入 Python 3 的人来说,这也是一个很好的练习。
Python 文档是了解每个版本之间差异的最佳参考。无论如何,为了方便读者,本节总结了最重要的内容。这并没有改变这一事实,即对于那些还不熟悉 Python 3 的人来说,文档是必读的(请参见https://docs.python.org/3.0/whatsnew/3.0.html 。
Python 3 引入的突破性变化通常可以分为几个组:
- 语法更改,其中删除/更改了一些语法元素,并添加了其他元素
- 标准库中的更改
- 数据类型和集合中的更改
使现有代码难以运行的语法更改最容易发现,它们将导致代码根本无法运行。具有新语法元素的 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'这些差异的列表有点长,任何新的 Python3.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现在是相对导入唯一可接受的语法。所有不以点字符开头的导入都被解释为绝对导入。sort()函数和列表的sorted()方法不再接受cmp参数。应该使用key参数。- 整数上的除法表达式,如 1/2 返回浮点。截断行为是通过类似于
1//2的//操作符实现的。好的是,它也可以与浮动一起使用,所以5.0//2.0 == 2.0。
打破标准库中的更改是继语法更改之后第二容易捕获的更改。Python 的每个后续版本都会添加、弃用、改进或完全删除标准库模块。这种过程在较旧版本的 Python(1.x 和 2.x)中也是常规的,因此在 Python3 中不会引起震动。在大多数情况下,根据被删除或重新组织的模块(如urlparse被移动到urllib.parse,它将在解释后的导入时间引发异常。这使得这些问题很容易被发现。无论如何,为了确保涵盖所有这些问题,完整的测试代码覆盖是必不可少的。在某些情况下(例如,当使用延迟加载的模块时),通常在导入时注意到的问题在某些模块作为函数调用在代码中使用之前不会出现。这就是为什么,确保每一行代码都在测试套件中实际执行是如此重要。
延迟加载模块
延迟加载的模块是在导入时未加载的模块。在 Python 中,import语句可以包含在函数中,因此导入将在函数调用时发生,而不是在导入时发生。在某些情况下,这种模块加载可能是一种合理的选择,但在大多数情况下,它是设计不良的模块结构的一种解决办法(例如,避免循环导入),通常应避免。当然,没有合理的理由延迟加载标准库模块。
当开发人员试图维护兼容性或只是将现有代码移植到 Python3 时,Python 如何表示数据类型和集合的更改需要付出最大的努力。虽然不兼容的语法或标准库更改很容易被注意到,并且最容易修复,但集合和类型中的更改要么不明显,要么需要大量重复工作。此类变更的清单很长,同样,官方文件是最好的参考。
尽管如此,本节仍必须讨论 Python 3 中处理字符串文本的方式的变化,因为这似乎是 Python 3 中最具争议和讨论的变化,尽管这是一个非常好的事情,现在使事情更加明确。
所有字符串文字现在都是 Unicode,bytes文字需要一个b或B前缀。对于 Python 3.0 和 3.1,使用u前缀(如u"foo")已被删除,并将引发语法错误。删除前缀是所有争议的主要原因。要创建在 Python 版本 2.x 的不同分支中兼容的代码非常困难,因为它依赖于这个前缀来创建 Unicode 文本。这个前缀在 Python3.3 中重新引入,以简化集成过程,尽管没有任何语法含义。
保持 Python 版本之间的兼容性是一项挑战。它可能会增加很多额外的工作,这取决于项目的规模,但肯定是可行和值得做的。对于打算在许多环境中重用的包,它是绝对必须具备的。没有定义良好且经过测试的兼容性边界的开源软件包不太可能流行,但是,永远不会离开公司网络的封闭的第三方代码可以从在不同环境中进行测试中受益匪浅。
这里应该注意的是,虽然本部分主要关注 Python 不同版本之间的兼容性,但这些方法适用于维护与外部依赖项(如不同的包版本、二进制库、系统或外部服务)的兼容性。
整个过程可分为三个主要领域,按重要性排序:
- 定义和记录目标兼容性界限以及如何管理这些界限
- 在每个环境中进行测试,每个依赖项版本都声明为兼容
- 实现实际的兼容性代码
声明被认为是兼容的内容是整个过程中最重要的部分,因为它使代码的用户(开发人员)能够对其工作方式以及将来如何更改抱有期望并做出假设。我们的代码可以在不同的项目中作为依赖项使用,这些项目可能也会努力管理兼容性,因此对其行为进行推理的能力至关重要。
虽然这本书试图总是给出一些选择,而不是对具体的选择给出一个绝对的建议,但这里有一个例外。到目前为止,定义未来兼容性如何变化的最佳方法是使用语义版本控制(对版本号进行适当的版本控制 http://semver.org/ ),或者很快,semver。它描述了一个被广泛接受的标准,用于通过仅由三个数字组成的版本说明符标记代码变更范围。它还提供了一些关于如何处理弃用策略的建议。以下是其摘要的摘录:
给定版本号MAJOR.MINOR.PATCH,增量:
- 进行不兼容的 API 更改时的
MAJOR版本 - 以向后兼容的方式添加功能时的
MINOR版本 - 一个
PATCH版本,当您进行向后兼容的 bug 修复时
预发布和构建元数据的附加标签可作为MAJOR.MINOR.PATCH格式的扩展提供。
说到测试,令人悲哀的事实是,为了确保代码与每个声明的依赖版本和每个环境(这里是 Python 版本)都兼容,必须在这些版本的每个组合中进行测试。当然,当项目有很多依赖项时,这可能是不可能的,因为在一个版本中,随着每个新依赖项的增加,组合的数量会快速增长。因此,通常需要进行一些权衡,以便运行完全兼容性测试不会花费太多时间。在第 10 章、测试驱动开发中介绍了一系列有助于在所谓矩阵中进行测试的工具,这些工具一般都会讨论测试。
使用遵循 semver 的项目的好处是,通常需要测试的只是主要版本,因为次要版本和补丁版本保证不包含向后不兼容的更改。只有在可以信任这些项目不会破坏这样的合同的情况下,这才是正确的。不幸的是,错误发生在每个人身上,向后不兼容的更改发生在许多项目中,甚至在补丁版本上。尽管如此,由于 semver 声明了对小版本和补丁版本更改的严格兼容性,因此打破它被认为是一个 bug,所以它可能会在补丁版本中修复。
如果兼容性的边界定义良好并经过严格测试,那么兼容性层的实现是最后也是最不重要的。仍然有一些工具和技术,每个对这样一个主题感兴趣的程序员都应该知道。
最基本的是 Python 的__future__模块。它将较新 Python 版本的一些功能移植回较旧的 Python 版本,并采用 import 语句的形式:
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 win32
Type "help", "copyright", "credits" or "license" for more information.
>>> type("foo") # old literals
<type 'str'>
>>> from __future__ import unicode_literals
>>> type("foo") # now is 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语句也无法可靠地处理多个标准库重组。令人高兴的是,有些工具旨在提供一致的即用兼容性。最常见的是六个(https://pypi.python.org/pypi/six/ 作为单个模块提供整个通用 2/3 兼容性样板文件。另一个有前途但不太受欢迎的工具是未来模块(http://python-future.org/ 。
在某些情况下,开发人员可能不希望在一些小软件包中包含其他依赖项。一种常见的做法是附加模块,该模块收集所有兼容性代码,通常命名为compat.py。下面是从python-gmaps项目(中提取的compat模块示例 https://github.com/swistakm/python-gmaps :
# -*- 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 兼容性的项目中也很流行,因为它是存储代码的一种非常方便的方式,可以处理与用作依赖项的包的不同版本的兼容性。
下载示例代码
您可以从您的帐户下载本书的示例代码文件 http://www.packtpub.com 。如果您在其他地方购买了本书,您可以访问http://www.packtpub.com/support 并注册,将文件直接通过电子邮件发送给您。
您可以通过以下步骤下载代码文件:
- 使用您的电子邮件地址和密码登录或注册我们的网站。
- 将鼠标指针悬停在顶部的支架选项卡上。
- 点击代码下载&勘误表。
- 在搜索框中输入图书名称。
- 选择要下载代码文件的书籍。
- 从您购买本书的下拉菜单中选择。
- 点击代码下载。
下载文件后,请确保使用的最新版本解压或解压缩文件夹:
- WinRAR/7-Zip for Windows
- 适用于 Mac 的 Zipeg/iZip/UnRarX
- 适用于 Linux 的 7-Zip/PeaZip
该书的代码包也托管在 GitHub 上的https://github.com/PacktPublishing/Expert-Python-Programming_Second-Edition 。我们在上还提供了丰富的书籍和视频目录中的其他代码包 https://github.com/PacktPublishing/ 。看看他们!
主要的 Python 实现是用 C 语言编写的,称为CPython。大多数人在谈论 Python 时都会提到它。随着语言的发展,C 语言的实现也随之改变。除了 C 之外,Python 还可以在其他一些试图跟上主流的实现中使用。其中大多数都是 CPython 背后的几个里程碑,但为在特定环境中使用和推广该语言提供了一个极好的机会。
有很多可选的 Python 实现可用。关于该主题的 Python Wiki 页面(https://wiki.python.org/moin/PythonImplementations 提供了 20 多种不同的语言变体、方言或 Python 解释器的实现,这些语言变体、方言或 Python 解释器是用 C 以外的语言构建的。其中一些仅实现了核心语言语法、功能、,和内置扩展,但至少有一些几乎完全兼容 CPython。要知道的最重要的一点是,尽管其中一些只是玩具项目或实验,但它们中的大多数都是为了解决一些实际问题而创建的——这些问题不是 CPython 无法解决,就是需要开发人员付出太多的努力。这些问题的例子如下:
- 在嵌入式系统上运行 Python 代码
- 与为运行时框架(如 Java 或.NET)或不同语言编写的代码集成
- 在 web 浏览器中运行 Python 代码
本节简要介绍了 Python 程序员目前可以使用的主观上最流行和最新的选择。
Stackless Python 宣传自己是 Python 的增强版。Stackless 之所以这样命名,是因为它避免了依赖 C 调用堆栈作为自己的堆栈。事实上,这是一个修改过的 CPython 代码,它还添加了一些在创建 Stackless 时核心 Python 实现中缺少的新特性。其中最重要的是由解释器管理的微线程,它是普通线程的廉价轻量级替代品,普通线程必须依赖于系统内核上下文切换和任务调度。
最新的可用版本是 2.7.9 和 3.3.5,分别实现了 Python 的 2.7 和 3.3 版本。Stackless 提供的所有附加功能通过内置的stackless模块在本发行版中作为一个框架公开。
Stackless 并不是 Python 最流行的替代实现,但值得了解,因为它引入的思想对语言社区产生了巨大的影响。核心交换功能是从 Stackless 中提取出来的,并作为一个名为greenlet的独立包发布,它现在是许多有用库和框架的基础。此外,它的大部分功能都是在 PyPy 中重新实现的,这是另一个 Python 实现,稍后将介绍。参见http://stackless.readthedocs.org/ 。
Jython 是该语言的 Java 实现。它将代码编译成 Java 字节码,并允许开发人员在其 Python 模块中无缝地使用 Java 类。Jython 允许人们在复杂的应用程序系统(例如 J2EE)上使用 Python 作为顶级脚本语言。它还将 Java 应用程序带入 Python 世界。制作 ApacheJackrabbit(基于 JCR 的文档库 API;参见http://jackrabbit.apache.org Python 程序中提供的是 Jython 允许的一个很好的例子。
Jython 的最新可用版本是 Jython 2.7,它对应于该语言的 2.7 版本。它被宣传为实现了几乎所有的核心 Python 标准库,并使用相同的回归测试套件。Jython3.x 的版本正在开发中。
Jython 与 CPython 实现的主要区别在于:
- True Java 的垃圾收集而不是引用计数
- 由于缺少GIL(全局解释器锁)允许在多线程应用程序中更好地利用多核
这种语言的实现的主要缺点是缺乏对 C Python 扩展 API 的支持,因此用 C 编写的任何 Python 扩展都不能与 Jython 一起使用。这在将来可能会改变,因为有计划在 Jython3.x 中支持 C Python 扩展 API。
一些 pythonweb 框架,比如 Pylons,被认为是在促进 Jython 的开发,使其在 Java 世界中可用。参见http://www.jython.org 。
IronPython 将 Python 引入.NET 框架。该项目由微软支持,IronPython 的首席开发人员在微软工作。它是促进一门语言的一个相当重要的实现。除了 Java 之外,.NET 社区是最大的开发者社区之一。还值得注意的是,微软提供了一套免费的开发工具,将 VisualStudio 转变为成熟的 Python IDE。这是作为名为PVTS(Python 工具,用于****Visual Studio)的 Visual Studio 插件分发的,并在 GitHub(上作为开源代码提供 http://microsoft.github.io/PTVS 。
最新的稳定版本是 2.7.5,它与 Python 2.7 兼容。与 Jython 类似,Python3.x 实现也有一些发展,但目前还没有稳定的版本。尽管.NET 主要在 Microsoft Windows 上运行,但也可以在 Mac OS X 和 Linux 上运行 IronPython。这要感谢 Mono,一个跨平台的开源.NET 实现。
IronPython 与 CPython 的主要区别或优势如下:
- 与 Jython 类似,缺少 GIL(全局解释器锁)允许在多线程应用程序中更好地利用多核
- 用 C#和其他.NET 语言编写的代码可以很容易地集成到 IronPython 中,反之亦然
- 可以通过 Silverlight 在所有主要 web 浏览器中运行
在谈到弱点时,IronPython 似乎与 Jython 非常相似,因为它不支持 C Python 扩展 API。这对于希望使用主要基于 C 扩展的包(如 numpy)的开发人员来说非常重要。有一个项目叫做 Ironcold(参考https://github.com/IronLanguages/ironclad 旨在允许与 IronPython 无缝地使用这些扩展,尽管其最后一个已知的受支持的版本是 2.6,开发似乎已经停止。参见http://ironpython.net/ 。
PyPy 可能是最激动人心的实现,因为它的目标是将 Python 重写为 Python。在 PyPy 中,Python 解释器本身就是用 Python 编写的。我们有一个 C 代码层来执行 Python 的 CPython 实现的具体工作。然而,在 PyPy 实现中,这个 C 代码层是用纯 Python 重写的。
这意味着您可以在执行期间更改解释器的行为,并实现在 CPython 中不容易实现的代码模式。
PyPy 目前的目标是与 Python 2.7 完全兼容,而 PyPy3 与 Python 3.2.5 版本兼容。
在过去,PyPy 之所以有趣主要是因为理论上的原因,它吸引了那些喜欢深入研究语言细节的人。它并没有普遍用于生产,但多年来已经发生了变化。如今,许多基准测试表明,令人惊讶的是,PyPy 通常比 CPython 实现快得多。本项目有自己的基准测试站点,跟踪使用数十种不同基准测试的每个版本的性能(参考http://speed.pypy.org/ )。它清楚地表明,启用 JIT 的 PyPy 至少比 CPython 快几倍。PyPy 的这一特性和其他特性使得越来越多的开发人员决定在其生产环境中切换到 PyPy。
PyPy 与 CPython 实现的主要区别在于:
- 使用垃圾收集代替引用计数
- 集成跟踪 JIT 编译器,可显著提高性能
- 应用程序级 Stackless 特性是从 Stackless Python 借用来的
与几乎所有其他可选的 Python 实现一样,PyPy 缺乏对 C Python 扩展 API 的全面官方支持。不过,它至少通过它的 CPyExt 子系统为 C 扩展提供了某种支持,尽管它的文档很少,功能也不完整。此外,社区内部正在努力将 NumPy 移植到 PyPy,因为它是最受欢迎的特性。参见http://pypy.org 。
作为一名专家,对所选编程语言的深入理解是最重要的。这对于任何技术都是正确的。尽管如此,如果不了解给定语言社区中的通用工具和实践,开发一个好的软件是非常困难的。Python 没有在其他语言中找不到的单一功能。因此,在直接比较语法、表达能力或性能时,总会有一个解决方案在一个或多个领域中更好。但是 Python 真正脱颖而出的领域是围绕该语言构建的整个生态系统。多年来,它的社区一直在完善标准实践和库,以帮助在更短的时间内创建更可靠的软件。
上述生态系统中最明显和最重要的部分是大量免费开源软件包的集合,这些软件包解决了大量问题。编写新软件总是一个昂贵而耗时的过程。能够重用现有代码而不是重新发明轮子大大减少了开发时间和成本。对一些公司来说,这是他们的项目在经济上可行的唯一原因。
由于这个原因,Python 开发人员在创建工具和标准方面投入了大量精力,以使用其他人创建的开源软件包。从虚拟隔离环境、改进的交互式 shell 和调试器开始,到帮助发现、搜索和分析PyPI(Python 软件包索引上提供的大量软件包的程序。
如今,许多操作系统都以 Python 作为标准组件。大多数 Linux 发行版和基于 Unix 的系统(如 FreeBSD、NetBSD、OpenBSD 或 OS X)都附带 Python,它们要么默认安装,要么通过系统包存储库提供。他们中的许多人甚至将其作为一些核心组件的一部分,Python 为 Ubuntu(Ubiquity)、Red Hat Linux(Anaconda)和 Fedora(Anaconda)的安装程序提供了动力。
由于这一事实,PyPI 中的许多软件包也可以作为本机软件包,由系统的软件包管理工具管理,如apt-get(Debian,Ubuntu)、rpm(Red Hat Linux)或emerge(Gentoo)。尽管应该记住,可用库的列表非常有限,而且与 PyPI 相比,它们大多已经过时。这就是为什么作为PyPA(Python 打包机构的推荐应该始终使用pip来获取最新版本的新包的原因。虽然它是从 CPython 的 2.7.9 和 3.4 版开始的独立软件包,但默认情况下,它与每个新版本捆绑在一起。安装新软件包非常简单,如下所示:
pip install <package-name>在其他功能中,pip允许强制特定版本的软件包(使用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 版本开始(以及 Python 2.7.9),始终可以使用ensurepip模块引导:
$ 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 的最新信息,请访问项目文档页面https://pip.pypa.io/en/stable/installing/ 。
pip可用于安装系统范围的软件包。在基于 Unix 和 Linux 的系统上,这将需要超级用户权限,因此实际调用将是:
sudo pip install <package-name>请注意,这在 Windows 上不是必需的,因为默认情况下它不提供 Python 解释器,并且 Windows 上的 Python 通常由用户手动安装,没有超级用户权限。
无论如何,不建议直接从 PyPI 安装系统范围的软件包,应该避免。这似乎与前面的陈述相矛盾,即使用pip是 PyPA 的建议,但有一些严重的原因。如前所述,Python 通常是通过操作系统软件包存储库提供的许多软件包的重要组成部分,可能为许多重要服务提供动力。系统分发维护人员在选择正确版本的包以匹配各种包依赖关系方面付出了大量的努力。通常,可以从系统的包存储库获得的 Python 包包含自定义补丁,或者只是为了确保与其他一些系统组件的兼容性而保持过时。使用pip强制更新这样一个包到一个破坏了向后兼容性的版本可能会破坏一些重要的系统服务。
仅仅为了开发目的在本地计算机上做这些事情也不是一个好借口。不计后果地使用pip这种方式几乎总是自找麻烦,最终会导致难以调试的问题。这并不意味着在全球范围内安装 PyPI 软件包是一件严格禁止的事情,但应该始终有意识地这样做,同时了解相关的风险。
幸运的是,有一个简单的解决方案可以解决这个问题—环境隔离。有各种工具允许在不同的系统抽象级别隔离 Python 运行时环境。其主要思想是将项目依赖关系与不同项目和/或系统服务所需的包隔离开来。这种方法的好处是:
- 它解决了“ProjectX 依赖于 Version1.X,但 ProjectY 需要 4.X”的困境。开发人员可以处理具有不同依赖关系的多个项目,这些依赖关系甚至可能发生冲突,而不存在相互影响的风险。
- 项目不再受其系统分发存储库中提供的包版本的约束。
- 不存在破坏依赖于某些包版本的其他系统服务的风险,因为新包版本仅在此类环境中可用。
- 作为项目依赖项的包列表可以很容易地“冻结”,因此很容易复制它们。
最简单、最轻量级的隔离方法是使用应用程序级虚拟环境。他们只关注于隔离 Python 解释器和其中可用的包。它们非常容易设置,并且通常只足以确保在开发小型项目和包时适当隔离。
不幸的是,在某些情况下,这可能不足以确保足够的一致性和再现性。对于这种情况,系统级隔离是对工作流的一个很好的补充,本章后面将介绍一些可用的解决方案。
有几种方法可以在运行时隔离 Python。最简单和最明显的方法是手动更改PATH和PYTHONPATH环境变量和/或将 Python 二进制文件移动到另一个位置,以影响它发现可用包的方式,并将其更改到我们希望存储项目依赖项的自定义位置,尽管这很难维护。幸运的是,有几种工具可以帮助维护虚拟环境以及如何在系统中存储已安装的软件包。主要有:virtualenv、venv和buildout。他们在引擎盖下所做的实际上与我们手动所做的相同。实际的策略取决于具体的工具实现,但一般来说,它们使用起来更方便,并且可以提供额外的好处。
到目前为止,Virtualenv 是这个列表中最流行的工具。它的名字仅仅代表虚拟环境。它不是标准 Python 发行版的一部分,因此需要使用pip获取它。它是值得在系统范围内安装的软件包之一(在基于 Linux 和 Unix 的系统上使用sudo)。
安装后,将使用以下命令创建新的虚拟环境:
virtualenv ENV此处,ENV应替换为新环境所需的名称。这将在当前工作目录路径中创建一个新的ENV目录。它将包含以下几个新目录:
bin/:这是存储其他包提供的新 Python 可执行文件和脚本/可执行文件的地方。lib/和include/:这些目录包含虚拟环境中新 Python 的支持库文件。新软件包将安装在ENV/lib/pythonX.Y/site-packages/中。
创建新环境后,需要在当前 shell 会话中使用 Unix 的 source 命令激活它:
source ENV/bin/activate这会影响当前 shell 会话的环境变量,从而更改当前 shell 会话的状态。为了让用户知道他已经激活了虚拟环境,它将通过在开头添加(ENV)字符串来更改 shell 提示。下面是创建新环境并激活它的示例会话,以说明这一点:
$ virtualenv example
New python executable in example/bin/python
Installing setuptools, pip, wheel...done.
$ source example/bin/activate
(example)$ deactivate
$ 关于virtualenv需要注意的重要一点是,它完全取决于存储在文件系统中的状态。它不提供任何额外的功能来跟踪应该在其中安装哪些软件包。这些虚拟环境不可移植,不应移动到其他系统/计算机。这意味着需要为每个新的应用程序部署从头开始创建新的虚拟环境。因此,virtualenv用户有一个很好的做法,将所有项目依赖项存储在requirements.txt文件中(这是命名约定),如下代码所示:
# lines followed by hash (#) are treated as a comments
# strict version names are best for reproducibility
eventlet==0.17.4
graceful==0.1.1
# for projects that are well tested with different
# dependency versions the relative version specifiers
# are acceptable too
falcon>=0.3.0,<0.5.0
# packages without versions should be avoided unless
# latest release is always required/desired
pytz有了这些文件,可以使用pip轻松安装所有依赖项,因为它接受需求文件作为其输出:
pip install -r requirements.txt需要记住的是,需求文件并不总是理想的解决方案,因为它没有定义依赖项的确切列表,只定义要安装的依赖项。因此,整个项目可以在开发环境中正常工作,但如果需求文件过时且不能反映环境的实际状态,则无法在其他环境中启动。当然,有pip freeze命令可以打印当前环境中的所有包,但不应盲目使用。它将输出所有内容,甚至是项目中未使用但仅为测试而安装的包。书中提到的另一个工具buildout解决了这个问题,因此对于一些开发团队来说,它可能是一个更好的选择。
对于 Windows 用户,Windows 下的virtualenv对其内部目录结构使用不同的命名。您需要使用Scripts/、Libs/和Include/而不是bin/、lib/、include/,以便更好地匹配该操作系统上的开发约定。用于激活/停用环境的命令也不同;您需要使用ENV/Scripts/activate.bat和ENV/Scripts/deactivate.bat而不是在activate和deactivate脚本上使用source。
虚拟环境很快建立起来,并成为社区内流行的工具。从 Python 3.3 开始,标准库支持创建虚拟环境。尽管命令行选项具有完全不同的命名约定,但其用法与 Virtualenv 几乎相同。新的venv模块提供了创建新虚拟环境的pyvenv脚本:
pyvenv ENV此处,ENV应替换为新环境所需的名称。此外,现在可以直接从 Python 代码创建新环境,因为所有功能都是从内置的venv模块公开的。其他用法和实现细节,如环境目录的结构和激活/停用脚本,基本上与 VirtualEnvironment 中的相同,因此迁移到此解决方案应该是简单而轻松的。
对于使用较新版本 Python 的开发人员,建议使用venv而不是 Virtualenv。对于 Python 3.3,切换到venv可能需要更多的努力,因为在这个版本中,默认情况下不会在新环境中安装setuptools和pip,所以用户需要手动安装。幸运的是,它在 Python3.4 中已经发生了变化,而且由于venv的可定制性,可以覆盖其行为。有关详细信息,请参见 Python 文档(参见https://docs.python.org/3.5/library/venv.html ),但一些用户可能会觉得这太棘手,并将继续使用 Virtualenv 来实现特定版本的 Python。
Buildout 是一个强大的工具,用于引导和部署用 Python 编写的应用程序。它的一些高级功能也将在书的后面部分解释。很长一段时间以来,它还被用作创建孤立 Python 环境的工具。由于 Buildout 需要一个声明性配置,每次依赖项发生更改时都必须更改该配置,而不是依赖于环境状态,因此这些环境更易于复制和管理。
不幸的是,这种情况已经改变。2.0.0 版之后的buildout包不再试图提供与系统 Python 安装的任何级别的隔离。隔离处理由其他工具(如 Virtualenv)来完成,因此仍然可以进行隔离构建,但事情变得有点复杂。构建必须在隔离环境中初始化,才能真正隔离。
与以前版本的 Buildout 相比,这有一个主要缺点,因为它依赖于其他解决方案进行隔离。处理此代码的开发人员不再能够确定依赖项描述是否完整,因为可以通过绕过声明性配置来安装某些包。当然,这个问题可以通过适当的测试和发布过程来解决,但它会给整个工作流增加一些复杂性。
总而言之,Buildout 不再是提供环境隔离的解决方案,但其声明性配置可以提高虚拟环境的可维护性和可再现性。
没有适合每个用例的最佳解决方案。一个组织的优点可能不适合其他团队的工作流程。此外,每个应用程序都有不同的需求。小型项目可以很容易地依赖于单一的virtualenv或venv,但大型项目可能需要buildout的额外帮助来执行更复杂的组装。
之前没有详细描述的是,早期版本的 Buildout(Buildout<2.0.0)允许在隔离环境中组装项目,其结果与 Virtualenv 提供的类似。不幸的是,该项目的 1.x 分支不再被维护,因此不鼓励将其用于该目的。
我建议尽可能使用venv模块而不是 Virtualenv。因此,这应该是针对 Python 版本 3.4 及更高版本的项目的默认选择。在 Python 3.3 中使用venv可能有点不方便,因为缺少对setuptools和pip的内置支持。对于针对更广泛 Python 运行时的项目(包括可选解释器和 2.x 分支),Virtualenv 似乎是最佳选择。
在大多数情况下,软件实现可以快速迭代,因为开发人员重用了大量现有的组件。不要重复你自己这是许多程序员的流行规则和座右铭。使用其他包和模块将它们包含在代码库中只是这种文化的一部分。在“重用组件”下还可以考虑二进制库、数据库、系统服务、第三方 API 等。甚至整个操作系统也应该被视为可重用的。
基于 web 的应用程序的后端服务是此类应用程序有多复杂的一个很好的例子。最简单的软件堆栈通常由几层组成(从最底层开始):
- 数据库或其它类型的存储器
- 用 Python 实现的应用程序代码
- HTTP 服务器,如 Apache 或 NGINX
当然,这样的堆栈可能更简单,但可能性很小。事实上,大型应用程序通常非常复杂,很难区分单层。大型应用程序可以使用许多不同的数据库,划分为多个独立的进程,并使用许多其他系统服务进行缓存、排队、日志记录、服务发现等。可悲的是,复杂性没有限制,代码似乎只是遵循热力学第二定律。
真正重要的是,并非所有的软件堆栈元素都可以在 Python 运行时环境级别上隔离。无论是 NGINX 之类的 HTTP 服务器还是 PostgreSQL 之类的 RDBMS,它们通常在不同的系统上有不同的版本。如果没有合适的工具,确保开发团队中的每个人都使用每个组件的相同版本是非常困难的。从理论上讲,一个团队中从事单个项目的所有开发人员都有可能在他们的开发箱中获得相同版本的服务。但是,如果他们不使用与生产环境中相同的操作系统,那么所有这些努力都是徒劳的。而强迫一个程序员去做他所钟爱的系统之外的事情是不可能的。
问题在于可移植性仍然是一个巨大的挑战。并非所有服务在生产环境中的工作方式都与在开发人员机器上的工作方式完全相同,而且这种情况不太可能改变。即使是 Python,在不同的系统上也会表现出不同的行为,尽管需要做大量的工作才能实现跨平台。通常,这是有很好的文档记录的,并且只发生在直接依赖于系统调用的地方,但是依靠程序员记住一长串兼容性怪癖的能力是一种非常容易出错的策略。
解决这个问题的一种流行方法是将整个系统隔离为应用程序环境。这通常是通过利用不同类型的系统虚拟化工具来实现的。当然,虚拟化会降低性能,但对于具有虚拟化硬件支持的现代计算机,性能损失通常可以忽略不计。另一方面,可能的收益列表非常长:
- 开发环境可以完全匹配生产中使用的系统版本和服务,这有助于解决兼容性问题
- 系统配置工具(如 Puppet、Chef 或 Ansible(如果使用))的定义可以重新用于开发环境的配置
- 如果这种环境的创建是自动化的,那么新雇用的团队成员可以很容易地加入到项目中
- 开发人员可以直接使用他们用于工作的操作系统上可能不可用的低系统级功能,例如 Windows 中不可用的FUSE(用户空间中的文件系统)
Vagrant 目前似乎是最流行的工具,它提供了一种简单方便的方法来创建和管理开发环境。它适用于 Windows、Mac OS 和一些流行的 Linux 发行版(请参阅https://www.vagrantup.com )。它没有任何其他依赖项。Vagrant 以虚拟机或容器的形式创建新的开发环境。具体的实现取决于虚拟化提供商的选择。VirtualBox 是默认的提供程序,它与 Vagrant 安装程序捆绑在一起,但也可以使用其他提供程序。最值得注意的选择是 VMware、Docker、LXC(Linux 容器)和 Hyper-V。
最重要的配置在一个名为Vagrantfile的文件中提供给 Vagrant。每个项目都应该是独立的。以下是它提供的最重要的东西:
- 虚拟化提供商的选择
- 框用作虚拟机映像
- 资源调配方法的选择
- 虚拟机和虚拟机主机之间的共享存储
- 需要在 VM 及其主机之间转发的端口
Vagrantfile的语法语言是 Ruby。示例配置文件提供了一个启动项目的好模板,并且有一个优秀的文档,因此不需要了解这种语言。可以使用单个命令创建模板配置:
vagrant init这将在当前工作目录中创建一个名为Vagrantfile的新文件。存储此文件的最佳位置通常是相关项目源的根。此文件已经是一个有效配置,将使用默认提供程序和基本框映像创建新 VM。默认情况下不启用任何资源调配。添加Vagrantfile后,新 VM 使用以下方式启动:
vagrant up初始启动可能需要几分钟,因为实际的盒子必须从 Web 下载。此外,每次启动已经存在的 VM 时,根据使用的提供程序、box 和系统性能,还可能需要一些初始化过程。通常,这只需要几秒钟。新的 Vagrant 环境启动并运行后,开发人员可以使用以下简写方式连接到 SSH:
vagrant ssh这可以在Vagrantfile位置下方的项目源代码树中的任何位置执行。为了方便开发人员,我们将在上面的目录中查找配置文件,并将其与相关的 VM 实例进行匹配。然后,它建立了安全的 shell 连接,因此开发环境可以像任何普通远程机器一样与之交互。唯一的区别是整个项目源代码树(根定义为Vagrantfile的位置)在虚拟机的文件系统/vagrant/下可用。
容器是完全机器虚拟化的替代品。这是一种轻量级的虚拟化方法,内核和操作系统允许运行多个独立的用户空间实例。操作系统在容器和主机之间共享,因此理论上它比完全虚拟化需要更少的开销。这样的容器只包含应用程序代码及其系统级依赖项,但从内部运行的进程的角度来看,它看起来像一个完全隔离的系统环境。
软件容器的流行主要归功于 Docker;这是可用的实现之一。Docker 允许以名为Dockerfile的简单文本文档的形式描述其容器。可以构建和存储来自此类定义的容器。它还支持增量更改,因此如果容器中添加了新内容,则不需要从头开始重新创建。
不同的工具,如 Docker 和 Vagrant,在功能上似乎有重叠,但它们之间的主要区别在于为什么要构建这些工具。正如前面提到的,Vagrant 主要是作为开发工具构建的。它允许用一个命令引导整个虚拟机,但不允许简单地打包、按原样部署或释放。另一方面,Docker 正是为准备完整的容器而建造的,这些容器可以作为一个整体包发送并部署到生产中。如果实施得当,此可以极大地改进产品部署过程。因此,只有在生产部署过程中也必须使用 Docker 和类似的解决方案(例如 Rocket)时,在开发过程中使用 Docker 和类似的解决方案才有意义。在开发过程中仅出于隔离目的使用它可能会产生太多的开销,并且还有一个缺点是不一致。
生产力工具有点模糊。一方面,几乎每一个在线发布和可用的开源代码包都是一种生产力提升工具,它为一些问题提供了现成的解决方案,因此没有人需要花费时间(理想地说)。另一方面,可以说 Python 的全部内容都是关于生产力的。毫无疑问,这两种观点都是正确的。这种语言和围绕它的社区中的几乎所有东西似乎都是为了使软件开发尽可能高效而设计的。
这就形成了一个积极的反馈循环。由于编写代码既有趣又简单,许多程序员利用空闲时间创建工具,使之更简单、更有趣。这一事实将在这里被用来作为一个非常主观和非科学的生产力工具定义的基础——一个使开发更容易、更有趣的软件。
从本质上讲,生产力工具主要关注开发过程的某些元素,如测试、调试和管理包,而不是它们帮助构建的产品的核心部分。在某些情况下,它们甚至可能不会在项目代码库的任何地方被引用,尽管它们每天都在使用。
本章前面已经讨论了最重要的生产力工具pip和venv。他们中的一些人有针对特定问题的软件包,如评测和测试,并且在书中有自己的章节。本节专门介绍其他确实值得一提的工具,但在本书中没有具体章节可以介绍这些工具。
Python 程序员花费大量时间在交互式解释器会话中。它非常适合于测试小代码片段、访问文档,甚至在运行时调试代码。默认的交互式 Python 会话非常简单,不提供很多特性,例如选项卡完成或代码内省帮助器。幸运的是,可以轻松地扩展和定制默认的 Python shell。
可以使用启动文件配置交互式提示。当它启动时,它寻找PYTHONSTARTUP环境变量并执行该变量指向的文件中的代码。一些 Linux 发行版提供默认启动脚本,该脚本通常位于主目录中。它被称为.pythonstartup。通常提供制表符完成和命令历史记录以增强提示,并基于readline模块。(你需要readline图书馆。)
如果您没有这样的文件,您可以轻松创建一个。下面是一个最简单的启动文件示例,它使用<Tab>键和历史记录添加完成:
# python startup file
import readline
import rlcompleter
import atexit
import os
# tab completion
readline.parse_and_bind('tab: complete')
# history file
histfile = os.path.join(os.environ['HOME'], '.pythonhistory')
try:
readline.read_history_file(histfile)
except IOError:
pass
atexit.register(readline.write_history_file, histfile)
del os, histfile, readline, rlcompleter在主目录中创建此文件,并将其命名为.pythonstartup。然后,使用文件路径在您的环境中添加一个PYTHONSTARTUP变量:
如果您正在运行 Linux 或 Mac OS X,最简单的方法是在主文件夹中创建启动脚本。然后,将其与PYTHONSTARTUP环境变量集合链接到系统 shell 启动脚本中。例如,Bash 和 Korn shell 使用.profile文件,您可以在其中插入一行,如下所示:
export PYTHONSTARTUP=~/.pythonstartup如果您正在运行 Windows,则可以在系统首选项中将新的环境变量设置为管理员,并将脚本保存在公共位置,而不是使用特定的用户位置。
在PYTHONSTARTUP脚本上编写可能是一个很好的练习,但是单独创建好的自定义 shell 是一个挑战,只有少数人能够抽出时间来完成。幸运的是,有一些定制的 pythonshell 实现极大地改善了 Python 中交互式会话的体验。
IPyhton(http://ipython.scipy.org 提供了一个扩展的 Python 命令 shell。在提供的功能中,最有趣的是:
- 动态对象内省
- 从提示符访问系统外壳程序
- 分析直接支持
- 调试设施
现在,IPython 是一个名为 Jupyter 的大型项目的一部分,该项目为交互式笔记本电脑提供可以用多种语言编写的实时代码。
bpython(http://bpython-interpreter.org/ 宣传自己是 python 解释器的一个奇特接口。以下是项目页面上的一些重点内容:
- 行内语法突出显示
- 类似于的 Readline 自动完成,并在键入时显示建议
- 任何 Python 函数的预期参数列表
- 自动缩进
- Python3 支持
ptpython(https://github.com/jonathanslenders/ptpython/ 是关于高级 Python shell 主题的另一种方法。在本项目中,核心提示实用程序实现作为一个名为prompt_toolkit的单独包提供(来自同一作者)。这允许您轻松创建各种美观的交互式命令行界面。
在功能上,它经常被比作 bpython,但主要区别在于它启用了与 IPython 的兼容模式,其语法支持其他功能,如%pdb、%cpaste或%profile。
代码调试是软件开发过程中不可或缺的一部分。许多程序员一生的大部分时间只能使用大量日志记录和print语句作为主要调试工具,但大多数专业开发人员更喜欢依赖某种调试器。
Python 已经附带了一个名为pdb的内置交互式调试器(请参阅https://docs.python.org/3/library/pdb.html 。它可以从现有脚本上的命令行调用,因此如果程序异常退出,Python 将进入后期调试:
python -m pdb script.py事后调试虽然有用,但并不涵盖所有场景。只有当应用程序存在时,如果出现错误,它才有用。在许多情况下,错误代码只是行为异常,但不会意外退出。在这种情况下,可以使用以下单行习惯用法在特定代码行上设置自定义断点:
import pdb; pdb.set_trace()这将导致 Python 解释器在运行时启动此行上的调试器会话。
pdb对于跟踪问题非常有用,乍一看,著名的 GDB(GNU 调试器)可能非常熟悉它。因为 Python 是一种动态语言,pdb会话与普通解释器会话非常相似。这意味着开发人员不仅限于跟踪代码执行,还可以调用任何代码,甚至执行模块导入。
不幸的是,由于它的根源(bdb),第一次使用pdb可能会有点让人不知所措,因为存在一些神秘的短信调试器命令,如h、b、s、n、j和r。如果有疑问,在调试器会话期间键入的help pdb命令将提供广泛的用法和附加信息。
pdb 中的调试器会话也非常简单,不提供诸如制表符完成或代码高亮显示之类的附加功能。幸运的是,PyPI 上很少有软件包可以从上一节提到的其他 Python shell 中提供此类功能。最显著的例子是:
ipdb:这是一个基于ipython的单独包ptpdb:这是一个基于ptpython的单独包bpdb:与bpython捆绑
对于 Python 开发人员来说,Web 上充满了有用的资源。最重要和最明显的是前面已经提到的,但为了保持列表的一致性,这里重复这些内容:
- Python 文档
- PyPI-Python 包索引
- Python 增强建议的 PEP 0 索引
其他资源,如书籍和教程是有用的,但往往会很快过时。社区积极策划或定期发布的资源不会过时。最值得推荐的两个是:
- 真棒的 python(https://github.com/vinta/awesome-python ),其中包括一份策划的流行软件包和框架列表
- Python 周刊(http://www.pythonweekly.com/ 是一份受欢迎的时事通讯,每周向订户提供数十个新的、有趣的 Python 包和资源
这两个资源将为读者提供数月的大量额外阅读。
本章从 Python2 和 Python3 之间的主题差异开始,并就如何处理当前社区中很大一部分在两个世界之间分裂的情况提出了建议。然后,谈到了 Python 开发的现代方法,这些方法的开发出人意料,主要是由于该语言的两个主要版本之间不幸的分裂。这些都是针对环境隔离问题的不同解决方案。本章最后简要总结了流行的生产力工具以及流行的资源,以供进一步参考。