您在这里:首页 > 深入 Python > 5 分钟回顾 | << >> | ||||
深入 Python从 Python 新手到专家 |
使用 Python 要做的第一件事就是安装它。或者您需要吗?
在 Windows 上,您有几种安装 Python 的选择。
在 Mac OS X 上,您有两种安装 Python 的选择:安装它或不安装它。您可能想要安装它。
Mac OS 9 不附带任何版本的 Python,但安装非常简单,而且只有一种选择。
通过访问 https://pythonlang.cn/ftp/python/ 并选择列出的最高版本号,然后选择该版本号内的 rpms/ 目录,下载最新的 Python RPM。然后下载版本号最高的 RPM。您可以使用 rpm 命令安装它,如下所示
如果您有幸运行 Debian GNU/Linux,则可以通过 apt 命令安装 Python。
如果您更喜欢从源代码构建,则可以从 https://pythonlang.cn/ftp/python/ 下载 Python 源代码。选择列出的最高版本号,下载 .tgz 文件),然后执行通常的 configure、make、make install 操作。
现在您已经安装了 Python,您正在运行的这个交互式 Shell 是什么?
您现在应该已经安装了适合您的 Python 版本。
这是一个完整的、可运行的 Python 程序。
与大多数其他语言一样,Python 也有函数,但它没有像 C++ 那样的单独头文件,也没有像 Pascal 那样的 interface/implementation 部分。当您需要一个函数时,只需声明它,如下所示
您可以通过为 Python 函数提供 docstring 来记录它。
函数,就像 Python 中的所有其他东西一样,是一个对象。
Python 函数没有显式的 begin 或 end,也没有花括号来标记函数代码的开始和停止位置。唯一的定界符是冒号 (:) 和代码本身的缩进。
Python 模块是对象,并且具有一些有用的属性。您可以在编写模块时使用它轻松地测试它们。下面是一个使用 if __name__ 技巧的示例。
Python 的内置数据类型之一是字典,它定义了键和值之间的一对一关系。
列表是 Python 的主力数据类型。如果您对列表的唯一经验是 Visual Basic 中的数组或(上帝保佑)Powerbuilder 中的数据存储,请为 Python 列表做好准备。
元组是不可变的列表。元组一旦创建就无法以任何方式更改。
与大多数其他语言一样,Python 也有局部变量和全局变量,但它没有显式的变量声明。变量通过赋值而存在,并在超出范围时自动销毁。
Python 支持将值格式化为字符串。尽管这可能包含非常复杂的表达式,但最基本的用法是使用 %s 占位符将值插入字符串。
Python 最强大的功能之一是列表推导式,它提供了一种通过对列表的每个元素应用函数将列表映射到另一个列表的紧凑方法。
您有一个以 key=value 形式出现的键值对列表,并且您想将它们连接成一个字符串。要将任何字符串列表连接成一个字符串,请使用字符串对象的 join 方法。
odbchelper.py 程序及其输出现在应该完全有意义了。
这是一个完整的、可运行的 Python 程序。您应该可以通过查看它来了解很多关于它的信息。带编号的行说明了 第 2 章,您的第一个 Python 程序 中涵盖的概念。如果您不理解代码的其余部分,请不要担心;这就是本章其余部分的内容。
Python 允许函数参数具有默认值;如果在调用函数时未提供参数,则该参数将使用其默认值。此外,可以通过使用命名参数以任何顺序指定参数。SQL Server Transact/SQL 中的存储过程可以做到这一点,因此如果您是 SQL Server 脚本专家,则可以跳过此部分。
Python 有一小组非常有用的内置函数。所有其他函数都被划分到模块中。这实际上是一个有意识的设计决定,是为了防止核心语言像其他脚本语言(咳咳,Visual Basic)那样变得臃肿。
您已经知道 Python 函数是对象。您不知道的是,您可以通过使用 getattr 函数获取对函数的引用,而无需在运行时知道其名称。
如您所知,Python 具有强大的功能,可以通过列表推导式将列表映射到其他列表(第 3.6 节,“映射列表”)。这可以与过滤机制结合使用,在过滤机制中,列表中的一些元素被映射,而其他元素则被完全跳过。
在 Python 中,and 和 or 按照您的预期执行布尔逻辑,但它们不返回布尔值;相反,它们返回它们正在比较的实际值之一。
Python 支持一种有趣的语法,可以让您动态定义单行迷你函数。这些所谓的 lambda 函数(借鉴自 Lisp)可以在需要函数的任何地方使用。
最后一行代码(也是您尚未解构的唯一一行代码)是完成所有工作的代码。但到目前为止,这项工作很容易,因为您需要的一切都已经按照您需要的方式设置好了。所有的多米诺骨牌都已就位;是时候把它们推倒了。
apihelper.py 程序及其输出现在应该完全有意义了。
这是一个完整的、可运行的 Python 程序。阅读模块、类和函数的 docstring,以概述该程序的功能及其工作原理。像往常一样,不要担心您不理解的东西;这就是本章其余部分的内容。
Python 有两种导入模块的方法。两者都很有用,您应该知道何时使用哪一种。一种方法是 import module,您已经在 第 2.4 节,“一切都是对象” 中看到了。另一种方法实现了相同的功能,但它有一些细微而重要的区别。
Python 是完全面向对象的:您可以定义自己的类,从您自己的类或内置类继承,并实例化您定义的类。
在 Python 中实例化类非常简单。要实例化一个类,只需像调用函数一样调用该类,并传递 __init__ 方法定义的参数。返回值将是新创建的对象。
如您所见,FileInfo 是一个像字典一样工作的类。为了进一步探讨这一点,让我们看一下 UserDict 模块中的 UserDict 类,它是 FileInfo 类的祖先。这没什么特别的;该类是用 Python 编写的,并存储在一个 .py 文件中,就像任何其他 Python 代码一样。特别是,它存储在您的 Python 安装的 lib 目录中。
除了普通的类方法之外,Python 类还可以定义许多特殊方法。特殊方法不是由您的代码直接调用(如普通方法),而是在特定情况下或使用特定语法时由 Python 为您调用。
Python 拥有的特殊方法不仅仅是 __getitem__ 和 __setitem__。其中一些方法可以让您模拟您甚至可能不知道的功能。
您已经了解了 数据属性,它们是由类的特定实例拥有的变量。Python 还支持类属性,它们是由类本身拥有的变量。
与大多数语言不同,Python 函数、方法或属性是私有的还是公共的完全由其名称决定。
这就是核心对象的技巧。您将在第 12 章中看到特殊类方法的实际应用,该章使用 getattr 创建远程 Web 服务的代理。
与许多其他编程语言一样,Python 通过 try...except 块进行异常处理。
Python 有一个内置函数 open,用于打开磁盘上的文件。open 返回一个文件对象,该对象具有用于获取有关已打开文件的信息和操作该文件的方法和属性。
与大多数其他语言一样,Python 也有 for 循环。您直到现在才看到它们的原因是 Python 擅长于许多其他事情,以至于您不需要经常使用它们。
与 Python 中的所有其他内容一样,模块也是对象。导入后,您始终可以通过全局字典 sys.modules 获取对模块的引用。
os.path 模块有几个用于操作文件和目录的函数。在这里,我们将介绍如何处理路径名和列出目录的内容。
再一次,所有的多米诺骨牌都已就位。您已经看到了每一行代码是如何工作的。现在让我们退一步,看看它们是如何组合在一起的。
第 5 章 中介绍的 fileinfo.py 程序现在应该完全清楚了。
如果您可以使用字符串函数完成您想做的事情,那么您应该使用它们。它们快速、简单、易读,而且快速、简单、可读的代码有很多优点。但是,如果您发现自己使用了许多不同的字符串函数以及 if 语句来处理特殊情况,或者如果您将它们与 split、join 和列表推导以奇怪的、不可读的方式组合在一起,那么您可能需要升级到正则表达式。
这一系列示例的灵感来自于几年前我在日常工作中遇到的一个现实问题,当时我需要在将街道地址导入到新系统之前,清理和标准化从旧系统导出的街道地址。(你看,我并不是凭空捏造这些东西;它实际上很有用。)这个例子展示了我如何解决这个问题。
您很可能见过罗马数字,即使您没有认出它们。您可能在老电影和电视节目的版权声明中看到过它们(“版权 MCMXLVI”而不是“版权 1946”),或者在图书馆或大学的献词墙上看到过它们(“成立于 MDCCCLXXXVIII”而不是“成立于 1888”)。您也可能在大纲和参考书目中看到过它们。这是一种表示数字的系统,它确实可以追溯到古罗马帝国(因此得名)。
在上一节中,您处理的是一个模式,其中同一个字符最多可以重复三次。在正则表达式中还有另一种表达方式,有些人认为这种方式更易读。首先看看我们在上一个例子中已经使用过的方法。
到目前为止,您一直在处理我所说的“紧凑型”正则表达式。正如您所见,它们难以阅读,即使您弄清楚了其中一个的作用,也不能保证您在六个月后还能理解它。您真正需要的是内联文档。
到目前为止,您一直专注于匹配整个模式。模式要么匹配,要么不匹配。但正则表达式的功能远不止于此。当正则表达式确实匹配时,您可以从中挑选出特定的部分。您可以找出匹配的位置。
这只是正则表达式功能的冰山一角。换句话说,即使您现在完全被它们淹没了,相信我,您还没有看到任何东西。
我经常在 comp.lang.python 上看到这样的问题:“如何列出我的 HTML 文档中的所有 [标题|图像|链接]?”“如何解析/转换/修改我的 HTML 文档的文本,但保留标签不变?”“如何一次性添加/删除/引用所有 HTML 标签的属性?”本章将回答所有这些问题。
HTML 处理分为三个步骤:将 HTML 分解成其组成部分,修改这些部分,然后将这些部分重新构建成 HTML。第一步由 sgmllib.py 完成,它是标准 Python 库的一部分。
要从 HTML 文档中提取数据,请对 SGMLParser 类进行子类化,并为要捕获的每个标签或实体定义方法。
SGMLParser 本身不会产生任何东西。它解析、解析、解析,并为它找到的每个有趣的东西调用一个方法,但这些方法什么也不做。SGMLParser 是一个 HTML 消费者:它接收 HTML 并将其分解成小的、结构化的片段。正如您在上一节中看到的,您可以对 SGMLParser 进行子类化,以定义捕获特定标签并生成有用内容的类,例如网页上所有链接的列表。现在,您将更进一步,定义一个类,该类捕获 SGMLParser 抛出的所有内容,并重建完整的 HTML 文档。用技术术语来说,这个类将是一个 HTML 生产者。
让我们从 HTML 处理中稍微离题一下,谈谈 Python 如何处理变量。Python 有两个内置函数,locals 和 globals,它们提供对本地和全局变量的基于字典的访问。
有一种替代的字符串格式化形式,它使用字典而不是值元组。
comp.lang.python 上的一个常见问题是“我有一堆没有引用的属性值的 HTML 文档,我想正确地引用它们。我该怎么做?”[4](这通常是由项目经理发现 HTML-是一种标准的宗教加入了一个大型项目,并宣称所有页面都必须通过 HTML 验证器验证而引发的。未引用的属性值是违反 HTML 标准的常见行为。)无论出于何种原因,未引用的属性值都可以通过将 HTML 传递给 BaseHTMLProcessor 来轻松修复。
Dialectizer 是 BaseHTMLProcessor 的一个简单(且愚蠢)的后代。它通过一系列替换来运行文本块,但它确保 <pre>...</pre> 块中的任何内容都能原封不动地通过。
现在是时候把你到目前为止所学到的所有知识都好好利用起来了。我希望你一直在专心听讲。
Python 为您提供了一个强大的工具 sgmllib.py,通过将其结构转换为对象模型来操作 HTML。您可以通过多种不同的方式使用此工具。
使用 XML 有两种基本方法。一种叫做 SAX(“XML 简单 API”),它的工作原理是一次读取一点 XML,并为它找到的每个元素调用一个方法。(如果您阅读了第 8 章,HTML 处理,这听起来应该很熟悉,因为这就是 sgmllib 模块的工作方式。)另一种叫做 DOM(“文档对象模型”),它的工作原理是一次性读取整个 XML 文档,并使用以树形结构链接的原生 Python 类创建它的内部表示。Python 具有用于这两种解析的标准模块,但本章只讨论如何使用 DOM。
实际上解析 XML 文档非常简单:一行代码。但是,在您开始编写代码之前,您需要稍微绕道谈谈包。
正如我所说,实际上解析 XML 文档非常简单:一行代码。从那里开始,您将何去何从取决于您自己。
Unicode 是一种表示来自世界各地不同语言的字符的系统。当 Python 解析 XML 文档时,所有数据都以 Unicode 形式存储在内存中。
通过遍历每个节点来遍历 XML 文档可能会很乏味。如果您正在 XML 文档深处寻找特定的内容,可以使用一个快捷方式快速找到它:getElementsByTagName。
XML 元素可以有一个或多个属性,一旦解析了 XML 文档,访问它们就非常简单。
好了,这就是核心 XML 内容。下一章将继续使用相同的示例程序,但重点关注使程序更灵活的其他方面:使用流进行输入处理,使用 getattr 进行方法调度,以及使用命令行标志允许用户在不更改代码的情况下重新配置程序。
Python 最大的优势之一是它的动态绑定,而动态绑定的一种强大用途是 类文件对象。
UNIX 用户已经熟悉标准输入、标准输出和标准错误的概念。本节面向其他人。
kgp.py 使用了一些技巧,这些技巧在您的 XML 处理中可能有用,也可能没有用。第一个技巧利用输入文档的一致结构来构建节点缓存。
解析 XML 文档的另一个有用技术是查找特定元素的所有直接子元素。例如,在语法文件中,ref 元素可以有多个 p 元素,每个元素都可以包含许多内容,包括其他 p 元素。您只想查找作为 ref 子元素的 p 元素,而不是作为其他 p 元素子元素的 p 元素。
第三个有用的 XML 处理技巧是根据节点类型和元素名称将代码分成逻辑函数。解析后的 XML 文档由各种类型的节点组成,每个节点都由一个 Python 对象表示。文档本身的根级别由 Document 对象表示。Document 然后包含一个或多个 Element 对象(用于实际的 XML 标记),每个对象可能包含其他 Element 对象、Text 对象(用于文本位)或 Comment 对象(用于嵌入式注释)。Python 可以轻松编写调度程序来分离每个节点类型的逻辑。
Python 完全支持创建可在命令行上运行的程序,并带有完整的命令行参数和用于指定各种选项的短样式或长样式标志。这些都不是 XML 特定的,但此脚本很好地利用了命令行处理,因此现在似乎是提及它的好时机。
您已经学习了很多内容。让我们退后一步,看看所有部分是如何组合在一起的。
Python 附带了用于解析和操作 XML 文档的强大库。minidom 获取一个 XML 文件并将其解析为 Python 对象,从而提供对任意元素的随机访问。此外,本章还展示了如何使用 Python 创建“真正的”独立命令行脚本,该脚本具有完整的命令行标志、命令行参数、错误处理,甚至可以从前一个程序的管道结果中获取输入。
您已经了解了HTML 处理和XML 处理,并在此过程中看到了如何下载网页和如何从 URL 解析 XML,但让我们深入探讨更通用的 HTTP Web 服务主题。
假设您要通过 HTTP 下载资源,例如联合 Atom 提要。但您不希望只下载一次;您希望每小时下载一次,以获取提供新闻提要的网站的最新消息。让我们先用快速而粗糙的方式来做,然后再看看如何做得更好。
您应该支持 HTTP 的五个重要特性。
首先,让我们打开 Python HTTP 库的调试功能,看看通过网络发送了什么。这在本章中非常有用,因为您将添加越来越多的功能。
改进 HTTP Web 服务客户端的第一步是用 User-Agent 正确标识自己。为此,您需要超越基本的 urllib 并深入研究 urllib2。
现在您知道了如何向 Web 服务请求添加自定义 HTTP 标头,让我们看看如何添加对 Last-Modified 和 ETag 标头的支持。
您可以使用不同类型的自定义 URL 处理程序来支持永久重定向和临时重定向。
您要支持的最后一个重要 HTTP 功能是压缩。许多 Web 服务都能够发送压缩数据,这可以将通过网络发送的数据量减少 60% 或更多。XML Web 服务尤其如此,因为 XML 数据的压缩率很高。
您已经看到了构建智能 HTTP Web 服务客户端的所有部分。现在让我们看看它们是如何组合在一起的。
openanything.py 及其功能现在应该完全清楚了。
您使用 Google,对吧?这是一个流行的搜索引擎。您是否曾经希望以编程方式访问 Google 搜索结果?现在您可以了。这是一个从 Python 搜索 Google 的程序。
与本书中的其他代码不同,本章依赖于未预装在 Python 中的库。
SOAP 的核心是能够调用远程函数。有许多公共访问的 SOAP 服务器提供简单的函数用于演示目的。
SOAP 库提供了一种简单的方法来查看幕后发生的事情。
SOAPProxy 类代理本地方法调用,并将其透明地转换为对远程 SOAP 方法的调用。正如您所见,这是一项艰巨的工作,而 SOAPProxy 可以快速而透明地完成这项工作。它没有做的是提供任何方法自省的方法。
与 Web 服务领域的许多事物一样,WSDL 也有着漫长而曲折的历史,充满了政治斗争和阴谋。我将完全跳过这段历史,因为它让我感到非常厌烦。还有其他一些标准试图做类似的事情,但 WSDL 赢了,所以让我们学习如何使用它。
让我们最终转向本章开头看到的示例代码,它做了一些比获取当前温度更有用和令人兴奋的事情。
当然,SOAP Web 服务的世界并非都是幸福和光明。有时会出错。
SOAP Web 服务非常复杂。该规范非常雄心勃勃,试图涵盖 Web 服务的许多不同用例。本章已经介绍了一些更简单的用例。
在前面的章节中,您通过立即查看代码并尝试尽快理解它来“深入了解”。现在您已经掌握了一些 Python 知识,您将退后一步,看看代码编写 之前 的步骤。
现在您已经完全定义了您期望从转换函数中获得的行为,您将做一些有点出乎意料的事情:您将编写一个测试套件,对这些函数进行测试,并确保它们的行为符合您的预期。您没有看错:您将编写代码来测试您尚未编写的代码。
这是罗马数字转换函数的完整测试套件,这些函数尚未编写,但最终将在 roman.py 中。所有这些是如何组合在一起的,目前还不清楚;这些类或方法都没有引用任何其他类或方法。这样做是有充分理由的,您很快就会看到。
单元测试最基本的部分是构建单个测试用例。测试用例回答有关它正在测试的代码的单个问题。
仅仅测试函数在输入良好的情况下能够成功是不够的,你还必须测试它们在输入错误的情况下会失败。而且不仅仅是任何类型的失败;它们必须以你预期的方式失败。
通常,你会发现一个代码单元包含一组互为倒数的函数,通常以转换函数的形式出现,其中一个将 A 转换为 B,另一个将 B 转换为 A。在这些情况下,创建一个“健全性检查”是很有用的,以确保你可以在不损失精度、不产生舍入误差或触发任何其他类型的错误的情况下将 A 转换为 B 并返回到 A。
现在单元测试已经完成,是时候开始编写测试用例试图测试的代码了。你将分阶段进行,这样你就可以看到所有单元测试都失败了,然后在你填补 roman.py 中的空白时,看着它们一个接一个地通过。
现在你已经搭建了 roman 模块的框架,是时候开始编写代码并通过测试用例了。
现在 toRoman 在输入良好(整数从 1 到 3999)的情况下表现正常,是时候让它在输入错误(其他所有情况)的情况下也表现正常了。
现在 toRoman 已经完成了,是时候开始编写 fromRoman 的代码了。由于将单个罗马数字映射到整数值的丰富数据结构,这并不比 toRoman 函数更难。
现在 fromRoman 在输入良好的情况下可以正常工作,是时候完成最后一块拼图了:让它在输入错误的情况下也能正常工作。这意味着要找到一种方法来查看一个字符串并确定它是否是一个有效的罗马数字。这本质上比在 toRoman 中验证数字输入 更难,但你有一个强大的工具可以使用:正则表达式。
尽管你尽了最大努力编写全面的单元测试,但错误还是会发生。我所说的“错误”是什么意思?错误就是你还没有编写的测试用例。
尽管你尽了最大努力去确定客户的需求,并从他们那里获取确切的需求,但需求还是会发生变化。大多数客户在看到产品之前并不知道自己想要什么,即使他们知道,他们也不擅长用足够精确的语言来表达他们的需求。即使他们这样做了,他们在下一个版本中还是会想要更多。因此,要准备好随着需求的变化更新你的测试用例。
全面单元测试的最大好处不是当你所有的测试用例最终都通过时的那种感觉,甚至也不是当别人指责你破坏了他们的代码而你实际上可以证明你没有做的时候的那种感觉。单元测试的最大好处是它给了你无情重构的自由。
一位聪明的读者阅读了上一节并将其提升到了一个新的水平。程序中最大的麻烦(和性能瓶颈)是正则表达式,这是必需的,因为你没有其他方法来分解罗马数字。但它们只有 5000 个;为什么不建立一个查找表,然后直接读取呢?当你意识到你根本不需要使用正则表达式时,这个想法就更好了。当你建立将整数转换为罗马数字的查找表时,你可以建立反向查找表将罗马数字转换为整数。
单元测试是一个强大的概念,如果实施得当,可以降低维护成本,并增加任何长期项目的灵活性。同样重要的是要明白,单元测试不是灵丹妙药,不是万能的,也不是银弹。编写好的测试用例很难,而且保持它们的更新需要纪律性(尤其是在客户要求修复关键错误时)。单元测试不能替代其他形式的测试,包括功能测试、集成测试和用户验收测试。但它是可行的,而且确实有效,一旦你看到它有效,你就会想知道没有它你是如何度过的。
在第 13 章,单元测试中,你学习了单元测试的理念。在第 14 章,测试先行编程中,你逐步了解了在 Python 中实现基本单元测试的过程。在第 15 章,重构中,你看到了单元测试如何使大规模重构更容易。本章将在这些示例程序的基础上继续讨论,但我们将更多地关注高级 Python 特定的技术,而不是单元测试本身。
从命令行运行 Python 脚本时,有时知道当前运行的脚本在磁盘上的位置是很有用的。
你已经熟悉了使用列表推导式来过滤列表。还有另一种方法可以实现同样的效果,有些人认为这种方法更具表达力。
现在你可能在挠头,想知道为什么这比使用 for 循环和直接函数调用更好。这是一个非常有效的问题。在很大程度上,这是一个视角问题。使用 map 和 filter 会迫使你将思维集中在数据上。
好了,哲学思考到此为止。让我们来谈谈动态导入模块。
你现在已经学到了足够多的知识来解构本章代码示例的前七行:读取目录并导入其中的选定模块。
regression.py 程序及其输出现在应该完全可以理解了。
我想谈谈复数名词。还有,返回其他函数的函数、高级正则表达式和生成器。生成器是 Python 2.3 中的新增功能。但首先,让我们来谈谈如何生成复数名词。
所以你在看单词,至少在英语中是字符串。你有一些规则,说你需要找到不同的字符组合,然后对它们做不同的事情。这听起来像是正则表达式的工作。
现在你要添加一个抽象级别。你一开始定义了一个规则列表:如果是这样,就那样做,否则就进入下一条规则。让我们暂时把程序的一部分复杂化,这样你就可以简化另一部分。
为每个匹配和应用规则定义单独的命名函数并不是真的必要。你永远不会直接调用它们;你在 rules 列表中定义它们,并通过它来调用它们。让我们通过匿名化这些函数来简化规则定义。
让我们把代码中的重复部分分解出来,以便更容易地定义新的规则。
你已经将所有重复的代码分解出来,并添加了足够的抽象,以便在字符串列表中定义复数规则。下一步是将这些字符串放在一个单独的文件中,在那里它们可以与使用它们的代码分开维护。
现在你可以谈谈生成器了。
在本章中,你讨论了几种不同的高级技术。并非所有技术都适用于所有情况。
优化代码的过程中有太多的陷阱,以至于很难知道从哪里开始。
关于优化 Python 代码,你需要知道的最重要的事情是,你不应该编写自己的计时函数。
Soundex 函数检查的第一件事是输入是否是一个非空的字母字符串。最好的方法是什么?
Soundex 算法的第二步是将字符按特定模式转换为数字。最好的方法是什么?
Soundex 算法的第三步是消除连续重复的数字。实现此目的的最佳方法是什么?
Soundex 算法的最后一步是用零填充短结果,并截断长结果。实现此目的的最佳方法是什么?
本章阐述了 Python 性能调优以及一般性能调优的几个重要方面。
<< 扩展阅读 |
| | |
技巧和窍门 >> |