附录 C. 技巧和窍门
优质
小牛编辑
125浏览
2023-12-01
附录 C. 技巧和窍门
第 1 章 安装 Python
第 2 章 第一个 Python 程序
- 2.1. 概览
在 Windows 的 ActivePython IDE 中, 可以选择 File->Run... (Ctrl-R) 来运行 Python 程序。输出结果将显示在交互窗口中。 在 Mac OS 的 Python IDE 中, 可以选择 Python->Run window... (Cmd-R) 来运行 Python 程序, 但首先要设置一个重要的选项。在 IDE 中打开 .py 模块, 点击窗口右上角的黑色三角, 弹出这个模块的选项菜单, 然后将 Run as __main__ 选中。 这个设置是同模块一同保存的, 所以对于每个模块您都需要这样做。 在 UNIX 兼容的操作系统中 (包括 Mac OS X), 可以通过命令行: python odbchelper.py 运行模块。 - 2.2. 函数声明
在 Visual Basic 中, 函数 (有返回值) 以 function 开始, 而子程序 (无返回值) 以 sub 开始。在 Python 中没有子程序。只有函数, 所有的函数都有返回值 (尽管可能为 None) , 并且所有的函数都以 def 开始。 在 Java, C++ 和其他静态类型语言中, 必须要指定函数返回值和每个函数参数的数据类型。 在 Python 中, 永远也不需要明确指定任何东西的数据类型。Python 会根据赋给它的值在内部将其数据类型记录下来。 - 2.3. 文档化函数
三重引号也是一种定义既包含单引号又包含双引号的字符串的简单方法, 就像 Perl 中的 qq/.../ 。 许多 Python IDE 使用 doc string 来提供上下文敏感文档信息, 所以当键入一个函数名时, 它的 doc string 显示为一个工具提示。这一点可以说非常有用, 但是它的好坏取决于您书写的 doc string 的好坏。 - 2.4. 万物皆对象
在 Python 中的 import 就像 Perl 中的 require。一旦 import 一个 Python 模块, 您可以使用 module.function 来访问它的函数;一旦您 require 一个 Perl 模块, 您可以使用 module::function 来访问它的函数。 - 2.5. 代码缩进
Python 使用硬回车来分割语句, 冒号和缩进来分割代码块。C++ 和 Java 使用分号来分割语句, 花括号来分割代码块。 - 2.6. 测试模块
与 C 一样, Python 使用 == 做比较, 使用 = 做赋值。 与 C 不一样, Python 不支持行内赋值, 所以不会出现想要进行比较却意外地出现赋值的情况。 在 MacPython 上, 需要一个额外的步聚来使得 if __name__ 技巧有效。 点击窗口右上角的黑色三角, 弹出模块的属性菜单, 确认 Run as __main__ 被选中。
第 3 章 内置数据类型
- 3.1. Dictionary 介绍
Python 中的 dictionary 就象 Perl 中的 hash (哈希数组)。在 Perl 中, 存储哈希值的变量总是以 % 字符开始;在 Python 中, 变量可以任意取名, 并且 Python 在内部会记录下其数据类型。 Python 中的 dictionary 象 Java 中的 Hashtable 类的实例。 Python 中的 dictionary 象 Visual Basic 中的 Scripting.Dictionary 对象的实例。 - 3.1.2. Dictionary 的修改
Dictionary 没有元素顺序的概念。说元素 “顺序乱了” 是不正确的, 它们只是序偶的简单排列。这是一个重要的特性, 它会在您想要以一种特定的, 可重复的顺序 (象以 key 的字母表顺序) 存取 dictionary 元素的时候骚扰您。有一些实现这些要求的方法, 它们只是没有加到 dictionary 中去。 - 3.2. List 介绍
Python 的 list 如同 Perl 中的数组。在 Perl 中, 用来保存数组的变量总是以 @ 字符开始;在 Python 中, 变量可以任意取名, 并且 Python 在内部会记录下其数据类型。 Python 中的 list 更象 Java 中的数组 (您可以简单的这样理解, 但 Python 中的 list 远比 Java 中的数组强大)。一个更好的类比是 ArrayList 类, 它可以保存任意对象, 并且可以在增加新元素时动态扩展。 - 3.2.3. 在 list 中搜索
在 2.2.1 版本之前, Python 没有单独的布尔数据类型。 为了弥补这一点, Python 在布尔环境 (如 if 语句) 中几乎接受所有东西, 遵循下面的规则: - 0 为 false; 其它所有数值皆为 true。
- 空串 ("") 为 false; 其它所有字符串皆为 true。
- 空 list ([]) 为 false; 其它所有 list 皆为 true。
- 空 tuple (()) 为 false; 其它所有 tuple 皆为 true。
- 空 dictionary ({}) 为 false; 其它所有 dictionary 皆为 true。
- 3.3. Tuple 介绍
Tuple 可以转换成 list, 反之亦然。内置的 tuple 函数接收一个 list, 并返回一个有着相同元素的 tuple。而 list 函数接收一个 tuple 返回一个 list。从效果上看, tuple 冻结一个 list, 而 list 解冻一个 tuple。 - 3.4. 变量声明
当一条命令用续行符 (“\”) 分割成多行时, 后续的行可以以任何方式缩近, 此时 Python 通常的严格的缩近规则无需遵守。如果您的 Python IDE 自由对后续行进行了缩近, 您应该把它当成是缺省处理, 除非您有特别的原因不这么做。 - 3.5. 格式化字符串
在 Python 中, 字符串格式化使用与 C 中 sprintf 函数一样的语法。 - 3.7. 连接 list 与分割字符串
join 只能用于元素是字符串的 list; 它不进行任何的类型强制转换。连接一个存在一个或多个非字符串元素的 list 将引发一个异常。 anystring.split(delimiter, 1) 是一个有用的技术, 在您想要搜索一个子串, 然后处理字串前面的东西 (即 list 中第一个元素) 和其后的东西 (即 list 中第二个元素) 时, 使用这个技术。
第 4 章 自省的威力
- 4.2. 使用可选参数和命名参数
调用函数时唯一需要做的事情就是为每一个必备参数指定值(以某种方式);以何种具体的方式和顺序都取决于你。 - 4.3.3. 内置函数
Python 提供了很多出色的参考手册,你应该好好的精读一下所有 Python 提供的必备模块。对于其它大部分语言,你会发现自己要常常回头参考手册或者 man 页来提醒自己如何使用这些模块,但是 Python 不同于此,它很大程度上是自文档化的。 - 4.7. 使用 lambda 函数
lambda 函数是一种风格问题。不一定非要使用它们;任何能够使用它们的地方,都可以定义一个单独的普通函数来进行替换。我将它们用在需要封装特殊的、非重用代码上,避免另我的代码充斥着大量单行函数。 - 4.8. 全部放在一起
在 SQL 中,你必须使用 IS NULL 代替 = NULL 进行 null 值比较。在 Python,你可以使用 == None 或者 is None 进行比较,但是 is None 更快。
第 5 章 对象和面向对象
- 5.2. 使用 from module import 导入模块
Python 中的 from module import * 象 Perl 中的 use module ;Python 中的 import module 象 Perl 中的 require module 。 Python 中的 from module import * 象 Java 中的 import module.* ;Python 中的 import module 象 Java 中的 import module 。 尽量少用 from module import * ,因为判定一个特殊的函数或属性是从哪来的有些困难,并且会造成调试和重构都更困难。 - 5.3. 类的定义
在 Python 中的 pass 语句就象 Java 或 C 中的大括号空集 ({})。 在 Python 中,类的基类只是简单地列在类名后面的小括号里。不象在 Java 中有一个特殊的象 extends 的关键字。 - 5.3.1. 初始化并开始类编码
习惯上,任何 Python 类方法的第一个参数(对当前实例的引用)都叫做 self。这个参数扮演着 C++ 或 Java 中的保留字 this 的角色,但 self 在 Python 中并不是一个保留字,它只是一个命名习惯。虽然如此,也请除了 self 之外不要使用其它的名字,这是一个非常坚固的习惯。 - 5.3.2. 了解何时去使用 self 和 __init__
__init__ 方法是可选的,但是一旦你定义了,就必须记得显示调用父类的 __init__ 方法(如果它定义了的话)。这样更是正确的:无论何时子类想扩展父类的行为,后代方法必须在适当的时机,使用适当的参数,显式调用父类方法。 - 5.4. 类的实例化
在 Python 中,创建类的实例只要调用一个类,仿佛它是一个函数就行了。不象 C++ 或 Java 有一个明确的 new 操作符。 - 5.5. 探索 UserDict: 一个封装类
在 Windows 下的 ActivePython IDE 中,你可以快速打开在你的库路径中的任何模块,使用 File->Locate... (Ctrl-L)。 Java 和 Powerbuilder 支持通过参数列表的重载,也就是 一个类可以有同名的多个方法,但这些方法或者是参数个数不同,或参数的类型不同。其它语言(最明显如 PL/SQL)甚至支持通过参数名的重载,也就是 一个类可以有同名的多个方法,这些方法有相同类型,相同个数的参数,但参数名不同。Python 两种都不支持,总之是没有任何形式的函数重载。一个 __init__ 方法就是一个 __init__ 方法,不管它有什么样的参数。每个类只能有一个 __init__ 方法,并且如果一个子类拥有一个 __init__ 方法,它 总是 覆盖父类的 __init__ 方法,甚至子类可以用不同的参数列表来定义它。 Python 的原作者 Guido 是这样解释方法覆盖的 “子类可以覆盖父类中的方法。因为方法没有特殊的优先级设置,在调用同一对象的另外方法时,父类中一个方法对另一个同类中的方法的调用,可能其实调用到的却是一个子类中覆盖父类同名方法的方法。(对于 C++ 程序员,所有的 Python 方法都非常有效)” 如果你不明白(它另我颇感困惑),不必在意。我想我要跳过它。 应该总是在 __init__ 方法中给一个实例的所有数据属性赋予一个初始值。这样做将会节省你在后面调试的时间,不必为捕捉因使用未初始化(也就是不存在)的属性而导致的 AttributeError 异常费时费力。 在 Python 2.2 之前的版本中,你不可以直接子类化字符串、列表以及字典之类的内建数据类型。 作为补偿, Python 提供封装类来模拟内建数据类型的行为,比如:UserString, UserList 和 UserDict。 通过混合使用普通和特殊方法, UserDict 类出色于模仿字典。 在 Python 2.2 和其后的版本中,你可以直接从 dict 内建数据类型继承。本书 fileinfo_fromdict.py 中有这方面的一个例子。 - 5.6.1. 获得和设置数据项
当在一个类中存取数据属性时,你需要限定属性名:self.attribute。当调用类中的其它方法时,你属要限定方法名:self.method。 - 5.7. 高级专用类方法
在 Java 中,通过使用 str1 == str2 可以决定两个字符串变量是否指向同一块物理内存位置。这就做 对象同一性,在 Python 中写为 str1 is str2。在 Java 中为了比较两个字符串值,你要使用 str1.equals(str2);在 Python 中,你要使用 str1 == str2。 某些 Java 程序员,他们已经被教授得认为,因为在 Java 中 == 是通过一致性而不是值来进行比较的,所以 Java 是更好的地方。这些人要转到 Python 上来可能要花些时间。在 Java 中,你通过 str1 == str2 来比较两个字符串是否指向同一物理内存位置。这被称作 对象确定(object identity), 在 Python 中被写作 str1 is str2。 在 Java 中比较字符串使用 str1.equals(str2) ,而在 Python 中,你使用 str1 == str2。那些素来认为 Java 中比较对象身份而不是值的 == 令世界变得更好的 Java 程序员可能对 Python 中的“这个缺少”接受起来有困难。 其它的面向对象语言仅让你定义一个对象的物理模型(“这个对象有 GetLength 方法”),而 Python 的专用类方法象 __len__ 允许你定义一个对象的逻辑模型(“这个对象有一个长度”)。 - 5.8. 类属性介绍
在 Java 中,静态变量(在 Python 中叫类属性)和实例变量(在 Python 中叫数据属性)两者是紧跟在类定义之后定义的(一个有 static 关键字,一个没有)。在 Python 中,只有类属性可以定义在这里,数据属性定义在 __init__ 方法中。 在 Python 中没有常量。如果你试图努力的话什么都可以改变。这一点满足 Python 的核心原则之一:坏的行为应该被克服而不是被取缔。如果你真正想改变 None 的值,也可以做到,但当无法调试的时候别来找我。 - 5.9. 私有函数
在 Python 中,所有的专用方法(象 __setitem__)和内置属性(象 __doc__)遵守一个标准的命名习惯:开始和结束都有两个下划下。不要对你自已的方法和属性用这种方法命名;到后面,它只会把你(或其它人)搞乱。
第 6 章 异常和文件处理
- 6.1. 异常处理
Python 使用 try...except 来处理异常,使用 raise 来引发异常。 Java 和 C++ 使用 try...catch 来处理异常,使用 throw 来引发异常。 - 6.5. 与 Directory 共事
只要有可能,你应该使用在 os 和 os.path 中的函数进行文件,目录,和路径的操作。这些模块是对平台相关模块的封装模块,所以象 os.path.split 这样的函数可以工作在 UNIX, Windows, Mac OS 和 Python 所支持的任一种平台上。
第 7 章 正则表达式
- 7.3. 个案研究:罗马字母
本章译者注:“被5整除的数”这个译法并不严谨,因为所有被10整除的数也能够被5整除,此处表达的含义是:那些包含有5的含义的罗马数字字符。 - 7.4. 使用{n,m} 语法
没有一个轻松的方法来确定两个正则表达式是否为等价的,你能采用的最好的办法就是列出很多的测试样例,确定这两个正则表达式对所有的相关输入都有相同的输出。在本书后面的章节,关于如何书写测试样例有更多的讨论。 - 7.4.1. 校验十位数和个位数
本章译者注:这个例子在正则表达式的匹配上没有问题,但是对于罗马数字的表示办法本身似乎有点问题,代表千位数的字符M,根据规定最多只能重复3次,但是在这个例子中重复了4次,但是这个罗马数字最后又表示3888,此处矛盾。不过,我们是为了搞清楚正则表达式的用法,罗马数字的表示法不是重点,因此从这个角度,这个例子没有问题。因此,在翻译的过程中保持了原文,大家在理解的时候需要注意一下这里。
第 8 章 HTML 处理
- 8.2. sgmllib.py 介绍
Python 2.0 存在一个 bug,即 SGMLParser 完全不能识别声明(handle_decl 永远不会调用),这就意味着 DOCTYPE 被静静地忽略掉了。在这错误在 Python 2.1 中改正了。 在 Windows 下的 ActivePython IDE 中,您可以在 “Run script” 对话框中指定命令行参数。用空格将多个参数分开。 - 8.4. BaseHTMLProcessor.py 介绍
HTML 规范要求所有非 HTML (象客户端的 JavaScript) 必须包括在 HTML 注释中,但不是所有的页面都是这么做的 (而且所有的最新的浏览器也都容许不这样做) 。BaseHTMLProcessor 不允许这样,如果脚本嵌入的不正确,它将被当作 HTML 一样进行分析。例如,如果脚本包含了小于和等于号,SGMLParser 可能会错误地认为找到了标记和属性。SGMLParser 总是把标记名和属性名转换成小写,这样可能破坏了脚本,并且 BaseHTMLProcessor 总是用双引号来将属性封闭起来 (尽管原始的 HTML 文档可能使用单引号或没有引号) ,这样必然会破坏脚本。应该总是将您的客户端脚本放在 HTML 注释中进行保护。 - 8.5. locals 和 globals
Python 2.2 引入了一种略有不同但重要的改变,它会影响名字空间的搜索顺序: 嵌套的作用域。 在 Python 2.2 版本之前,当您在一个嵌套函数 或 lambda 函数 中引用一个变量时,Python 会在当前 (嵌套的或 lambda) 函数的名字空间中搜索,然后在模块的名字空间。Python 2.2 将只在当前 (嵌套的或 lambda) 函数的名字空间中搜索,然后是在父函数的名字空间中搜索,接着是模块的名字空间中搜索。Python 2.1 可 以两种方式工作,缺省地,按 Python 2.0 的方式工作。但是您可以把下面一行代码增加到您的模块头部,使您的模块工作起来象 Python 2.2 的方式: from __future__ import nested_scopes
使用 locals 和 globals 函数,通过提供变量的字符串名字您可以动态地得到任何变量的值。这种方法提供了这样的功能: getattr 函数允许您通过提供函数的字符串名来动态地访问任意的函数。 - 8.6. 基于 dictionary 的字符串格式化
使用 locals 来应用基于 dictionary 的字符串格式化是一种方便的作法,它可以使复杂的字符串格式化表达式更易读。但它需要花费一定的代价。在调用 locals 方面有一点性能上的问题,这是由于 locals 创建了局部名字空间的一个拷贝 引起的。
第 9 章 XML 处理
- 9.2. 包
一个包是一个其中带有特殊文件 __init__.py 的目录。__init__.py 文件定义了包的属性和方法。其实它可以什么也不定义;可以只是一个空文件,但是必须要存在。如果 __init__.py 不存在,这个目录就仅仅是一个目录,而不是一个包,它就不能被导入或者包含其它的模块和嵌套包。 - 9.6. 访问元素属性
这部分由于某个涵义重叠的术语可能让人有点糊涂。在一个 XML 文档中,元素可以有属性,而 Python 对象也有属性。当你解析一个 XML 文档时,你得到了一组 Python 对象,它们代表 XML 文档中的所有片段,同时有些 Python 对象代表 XML 元素的属性。但是表示(XML)属性的(Python)对象也有(Python)属性,它们用于访问对象表示的(XML)属性。我告诉过你它让人糊涂。我会公开提出关于如何更明显地区分这些不同的建议。 类似于字典,一个 XML 元素的属性没有顺序。属性可以以某种顺序偶然列在最初的 XML 文档中,而在 XML 文档解析为 Python 对象时,Attr 对象以某种顺序偶然列出,这些顺序都是任意的,没有任何特别的含义。你应该总是使用名称来访问单个属性,就像字典的键一样。
第 10 章 Scripts 和 Streams
第 11 章 HTTP Web 服务
- 11.6. 处理 Last-Modified 和 ETag
在这些例子中, HTTP 服务器同时支持 Last-Modified 和 ETag 头信息, 但并非所有的服务器皆如此。 作为一个 web 服务的客户, 你应该为支持两种头信息做准备, 但是你的程序也应该为服务器仅支持其中一种头信息或两种头信息都不支持而做准备。
第 12 章 SOAP Web 服务
第 13 章 单元测试
- 13.2. 深入
Python 2.1 和之后的版本已经包含了 unittest。Python 2.0 用户则可以从 pyunit.sourceforge.net下载。
第 14 章 以测试优先为原则的编程
- 14.3. roman.py, 第 3 阶段
全面的单元测试能够告诉你的最重要的事情是什么时候停止编写代码。当一个函数的所有单元测试都通过了,停止编写这个函数。一旦一个完整模块的单元测试通过了,停止编写这个模块。 - 14.5. roman.py, 第 5 阶段
当所有测试都通过了,停止编程。
第 15 章 重构
- 15.3. 重构
在需要多次使用同一个正则表达式的情况下,应该将它进行编译以获得一个 pattern 对象,然后直接调用这个 pattern 对象的方法。
第 16 章 有效编程(Functional Programming)
- 16.2. 找到路径
传递给 os.path.abspath 的路径名和文件名可以不存在。 os.path.abspath 不仅构建完整路径名,还能格式化路径名。 这意味着如果你正工作于 /usr/ 目录, os.path.abspath('bin/../local/bin') 将会返回 /usr/local/bin 。 它以尽可能简单的方式格式化路径名。 如果你只是希望简单地返回这样的格式化路径名而不需要完整路径名可以使用 os.path.normpath。 就像 os 和 os.path 模块的其他函数, os.path.abspath 是跨平台的。 如果你是在 Windows (使用反斜杠作为路径符号)或 Mac OS (使用冒号)上运行,它们同样工作,只是将获得与我稍有不同的结果。 os 的所有函数都是这样的。
第 17 章 动态函数
第 18 章 性能优化
- 18.2. 使用 timeit 模块
你可以在命令行使用 timeit 模块来测试一个已存在的 Python 程序,而不需要修改代码。在 http://docs.python.org/lib/node396.html 查看文档中关于命令行选项的内容。 timeit 模块只有在你知道那段代码需要优化时使用。 如果你有一个很大的 Python 程序并且不知道你的性能问题所在,到 查看 hotshot 模块。