《Python编程金典》读书笔记上(ZZ)
时间:2007-09-17 来源:pascal4123
《Python编程金典》读书笔记
整理:Jims of 肥肥世家
第一次发布时间:2004年5月26日
Table of Contents
1. 绪论2. python编程概述2.1. 知识点2.2. 良好的编程习惯2.3. 常见编程错误2.4. 测试和调试提示2.5. 移植性提示3. 控制流程3.1. 知识点3.2. 良好的编程习惯3.3. 常见编程错误3.4. 移植性提示3.5. 软件工程知识4. 函数4.1. 知识点4.2. 良好的编程习惯4.3. 常见编程错误4.4. 移植性提示4.5. 软件工程知识4.6. 性能提示5. 列表、元组和字典5.1. 知识点 6. 公共网关接口(CGI)入门6.1. 知识点7. 基于面向对象的编程7.1. 知识点7.2. 良好的编程习惯7.3. 常见编程错误7.4. 测试和调试提示7.5. 软件工程知识7.6. 性能提示8. 自定义类8.1. 知识点8.2. 良好的编程习惯8.3. 常见编程错误8.4. 软件工程知识8.5. 性能提示9. 面向对象编程:继承9.1. 知识点9.2. 常见编程错误9.3. 软件工程知识9.4. 性能提示10. 图形用户界面组件(一)10.1. 知识点10.2. 良好的编程习惯10.3. 常见编程错误10.4. 界面知识11. 图形用户界面组件(二)11.1. 知识点11.2. 测试和调试提示11.3. 界面知识12. 异常处理12.1. 知识点12.2. 良好的编程习惯12.3. 常见编程错误12.4. 测试和调试提示12.5. 软件工程知识12.6. 性能提示13. 字符串处理和正则表达式13.1. 知识点13.2. 良好的编程习惯13.3. 性能提示14. 文件处理和序列化14.1. 知识点14.2. 良好编程习惯14.3. 常见编程错误14.4. 性能提示List of Examples
15.1. sort.xml15.2. sorting.xsl16.1. 动态生成XML内容16.2. 一个XML论坛的例子Chapter 1. 绪论
Chapter 2. python编程概述
Table of Contents
2.1. 知识点2.2. 良好的编程习惯2.3. 常见编程错误2.4. 测试和调试提示2.5. 移植性提示2.1. 知识点
-
raw_input是python的内建函数,要求用户输入,输入结果是一个字符串。 example: test = raw_input("please input a number:\n")
-
python是一种区分大小写的语言。
-
id函数返回变量内存位置,type函数返回变量类型。
-
在python2.2前,只提供一种除法运算符(/),运算的行为(即是Floor整数除法,还是 True浮点除法)是由操作数的类型来决定的。如果操作数全是整数,就执行Floor除法。如 一个或两个操作数是浮点数,就执行True浮点除法。
-
在python 2.2后的所有版本中,设计者决定去除(/)的随意性。决定采用两个操作符,其 中/执行True除法;//执行Floor除法。但这样会造成旧版本的程序出错,所以设计者采取了 一种折衷的办法。如果不先声明,python还是使用旧的/操作符。如果要使用新的方法就要 进行声明,声明方式是: from __future__ import division,这样就可以用/ 和 //了。
-
格式化字符串 print "test is %d" % integer1,表示方法和c语言差不多。
-
如果语句太长需要用到“\”这个续行符。
-
在过程式编程中,程序员把重点放在写函数上,用于执行一些任务的行动被组合成函数, 不同的函数进一步进行组合,即构成程序。
-
采用面向对象编程,程序员的工作主要放在创建自已的“类”上。每个类包含数据及一 系列函数。类的数据组件被称为数据成员或属性,类的函数组件则称为方法。
-
重用,重用,再重用是影响软件开发三大因素。就如影响房地产价格三大因素是地段, 地段,不是地段。
2.2. 良好的编程习惯
-
在程序中使用丰富的注释。注释有助于其他程序员理解程序,有助于程序员调试,并列 出有用的信息。以后修改或更新代码时,注释还有助于你理解自已当初编写的程序。
-
每个程序都应以一条注释开始,描述该程序的用途。
-
加一些空行来增强程序的可读性。
-
有意义的变量名可改善程序的“自编档能力”,也就是说,只需读一读程序,就能轻松 理解它。
-
避免标识符以下划线和双下划线开头,因为python解释器可能保留了那些名称,供内部 使用。
-
在二元运算符两端添加一个空格。这样可以突出运算符,增强程序的可读性。
-
和代数一样,可在表达式中添加原本不需要的括号,使其更清晰。
2.3. 常见编程错误
-
试图访问一个未赋值的变量,会产生运行时错误。
-
不要把“==”相等和“=”赋值运算符操混了。赋值符号“=”不能出现在if等的条件 语句中。
-
忘记在if结构中插入冒号是语法错误。if a == b: ...
-
不要忘记了缩进格式,否则会出现语法错误。由于tab键在不同系统中的长度不同,所 以建议将3个空格定为一个缩进级别。如果在一个程序中缩进量不同,会造成语法错误。
2.4. 测试和调试提示
-
使用 -i 选项(python -i test.py)。会导致编译器在执行了文件中的语句后进行交互模式, 这非常适用于调试程序。
-
为了避免难以察觉的错误,务必在程序中采用统一和正确的缩进。
2.5. 移植性提示
-
预计在python 3.0中,运算符/只执行True除法。3.0发布后,程序需更新自已的程序。
Chapter 3. 控制流程
Table of Contents
3.1. 知识点3.2. 良好的编程习惯3.3. 常见编程错误3.4. 移植性提示3.5. 软件工程知识3.1. 知识点
-
所谓“算法”,是指解决一个问题的“过程”,它包含两个含义,1是要采取的行动,2 是采取这些行动的顺序。
-
研究表明,只要三种结构就可以写出所有程序,这三种结构是:顺序结构,选择结构以 及重复结构。
-
算法求精,就是把算法用伪代码逐层分解成可以用python程序实现的过程。
-
+=符号将符号右边的表达式的值加到左边的变量上,再将结果存回左边的变量。变量要 先初始化,如果没有会出错。
-
range(0,10,1)代表一个从0到9 共10个元素的序列,自增量为1。
-
与非结构化程序相比,结构化编程所生成的程序要容量理解得多,所以更易测试、调试 和修改,而且不易出错。
3.2. 良好的编程习惯
-
初始化所有变量。
-
在信号值控制的循环中,当提示输入时,应明确指明信息值是哪一个。
-
当执行除法运算时,如除数可能为零,请务必明确检测。关在程序中进行相应处理,不 要任由错误发生。
-
在每个控制结构前后各留一个空行,将其同程序的其余部份区分开。
-
嵌套级别过多,会使程序难以理解,通常应将嵌套控制在3级以内。
-
避免在for循环主体更改控制变量的值,这有可能导致不易发现的逻辑错误。
3.3. 常见编程错误
-
将所有浮点数假设为是精确的,会导致不正确的结果。浮点数在大多数计算机中只是近 似数。
-
在赋值符号左边的变量初始化之前试图使用增量赋值是错误的。
-
如果忘记range函数返回的序列的第一个值是0,可以导致差1错误。
3.4. 移植性提示
-
python 2.0开始引入增量赋值符号,在老版本中python中使用增量赋值符号是语法错误。
3.5. 软件工程知识
-
经验表明,用计算机解决问题最有效的办法是为解决方案开发一种算法。一旦开发出正 确的算法,通常能根据它方便地生成一个能实际工作的python程序。
-
在从事大型的、复杂的项目时,一定要开发算法。这样才可能不会导致严重错误,从而 推迟项目进度。
Chapter 4. 函数
Table of Contents
4.1. 知识点4.2. 良好的编程习惯4.3. 常见编程错误4.4. 移植性提示4.5. 软件工程知识4.6. 性能提示4.1. 知识点
-
python的程序组件包括函数、类、模块和包。“模块”是包含函数和类定义的文件。许多 模块可以组合成一个集合,称为“包”。
-
模块位于python安装目录的库目录下。在unix/linux下,是/usr/lib/python2.2或 /usr/local/lib/python2.2,在windows上,则是\python\lib。
-
函数定义中创建的所有变量都是“局部变量”--只存在于声明它们的函数中。
-
python定义了3个命名空间,分别是局部(local),全局(global)和内建(built-in)。程 序访问标识符的值时,python会按特定顺序搜索命名空间(即按局部,全局和内建顺序)。
-
import random as randomModule 指定引用名。现在可以用randomModule来引用random 中的函数。
-
重复使用重复的结构,如for和while;递归使用选择结构,如if和if/else。之间的差别 是,重复采用一个重复结构,而递归采用重复的函数调用。两者都要进行终止测试:重复会 在循环继续条件为false时终止;递归在识别出基本条件是终止。由计数器控制的重复和递 归都是逐渐终止:重复会不断改变一个计数器,直到计数器的值使循环继续条件变为false; 递归则不断对原始问题进行简化,直到抵达基本条件。重复和递归都可无休止地进行:如果 循环继续检测永远都不能变成false,会发生无限循环;如果递归调用永远不能将问题简化 成基本条件,会发生无穷递归。
-
函数的默认参数设置需在def语句中定义。如:def test(aa =1,bb=2,cc=3):xxx 。
-
关键字参数可以不按位置顺序出现在函数调用中。用keyword = value方式就可以了。
4.2. 良好的编程习惯
-
尽快熟悉核心python模块提供的函数和类集合。
-
避免变量名遮蔽外层作用域中的名称。为此,要注意避免标识符与内建命名空间中的标 识符同名,并避免在程序中使用重复的标识符。
-
使用默认参数可简化函数调用的编写,但有的程序员认为,显式指定所有参数会使程序 更易读。
4.3. 常见编程错误
-
用局部命名空间中的一个标识符遮蔽模块或内建命名空间中的一个标识符,可能引起逻 辑错误。
-
默认参数必须全部靠右。省略非靠右的参数是语法错误。
4.4. 移植性提示
-
使用核心python模块中的函数,通常可使用程序更易移植。
4.5. 软件工程知识
-
避免重复别人的劳动。尽量使用标准库模块函数,不要写新函数。这样可加快程序开发 进度,并增强可靠性。因为你所使用的是经良好设计和测试的代码。
-
每个函数都应该只限执行单一的、良好定义的任务,函数名应清楚地描述那个任务。
-
如果实在想不出能准确表达函数作用的名称,就表明函数可能执行了太多的分散任务, 通常,最好把这种函数分解成多个更小的函数。
-
程序应写为若干个小函数的集合。这样使程序更易编写、调试、维护和修改。
-
如函数需要大量的参数,表明它执行的任务可能过多。请考虑将函数分解成更小的函数, 令其执行单独的任务。函数的def语句尽可能不超过一行。
-
采用递归方式能解决的任何问题也可采用重复方式(非递归方式)解决。如果递归方式 能够更自然地反映问题,并使程序易于理解和调试,通常应该首选递归方式。通常,只需几 行代码就可完成一个递归方式,重复方式则相反,它需要大量的代码来实现。选择递归的另 一个原因是,重复方案也许不是很直观。
-
采用清晰的、层次清楚的方式对程序进行“函数化”,有助于保证良好的软件工程,但 性能上要付出一定代价。
4.6. 性能提示
-
不要试图改写现成的模块函数使其更高效,因为这些函数已非常完美了。
-
一般不要编写会造成调用次数成指数级增加的“斐波拉契”式递归程序。
-
避免对性能要求高的时候使用递归。递归调用既费时、又耗内存。
-
一个由多个函数构成的程序,与一个没有任何函数的一体式程序相比,会产生大量的函 数调用,这些函数调用会占用大量的处理器时间和内存。但另一方面,一体式程序的编程、 测试、调试和维护都比较复杂。因此对程序进行函数化时要综合考虑。保证能兼顾良好的性 能和软件工程。
Chapter 5. 列表、元组和字典
Table of Contents
5.1. 知识点5.1. 知识点
-
Python支持3种基本序列数据类型:字符串(string),列表(list)和元组(tuple)。
-
“映射”在其它语言中称为关联数据或“哈希”,是用于存储“键-值”对的数据结构。 python支持一种映射数据类型,字典。
-
创建序列:aString = "";aList = [];aTuple = ()。列表是可变序列,字符串和元组是不可 序列。
Chapter 6. 公共网关接口(CGI)入门
Table of Contents
6.1. 知识点6.1. 知识点
-
CGI可用于几乎任何程序语言或脚本语言,比如C,PERL和PYTHON。
-
最常见的HTTP请求类型是GET AND POST。这些请求从WEB服务器获取资源,并将 客户表单数据发送给WEB服务器。get请求将表单内容作为URL的一部份发送。大多数 WEB服务器将GET请求查询字符串限制在1024个字符以内。如果查询字符串超过这个限 制,就必须使用POST请求。POST请求中发送的数据不是URL的一部份,用户看不到它 们。如果表单包含许多字段,那通常由POST请求进行提交。一些敏感的表单字段,如用户 名和密码,也通常使用这种请求类型来发送。GET请求的最简单形式的格式为GET /books/downloads.html HTTP/1.1。服务器收到请求后,会发送一个HTTP标头,如 Content-type:text/html。表明MIME类型,然后服务器发送请求的HTML/XHTML文档中文 本(DOWNLOADS.HTML)。
-
web应用程序采用两类脚本编程,服务器端和客户端。CGI脚本是服务器端脚本的一个 例子,客户端脚本的一个例子是javascript。
Chapter 7. 基于面向对象的编程
Table of Contents
7.1. 知识点7.2. 良好的编程习惯7.3. 常见编程错误7.4. 测试和调试提示7.5. 软件工程知识7.6. 性能提示7.1. 知识点
-
在过程式语言中,基本编程单元是“函数”,在面向对象语言中,基本编程单元是“类”, 最终要通过它来实例化(即创建)对象。
-
__init__方法是类的“构造函数”方法。每次创建类的一个对象时,都会执行它的构造 函数。它会初始化对象属性,并返回None。
-
包括构造函数在内的所有方法至少要指定一个参数。该参数代表要调用其方法的类的对 象。人们常把这个参数称为“类实例对象”。但由于这术语容易混淆,所以我们将任何方法 的第一个参数都称为“对象引用参数”,或简称“对象引用”。方法必须通过对象引用来访问 从属于类的属性以及其它方法。按照约定,对象引用参数称为self。
-
类的特殊属性。
-
__bases__ 包含基类的一个元组,类可以从这些基类直接继承。如果类不从其他类继承, 元组就会为空。
-
__dict__ 与类的命名空间对应的一个字典。其中每个键-值对都代表在命名空间中的一个标 识符及其值。
-
__doc__ 类的文档字符串。如果类没有指定文档化字符串,值为None。
-
__module__ 包含模块(文件)名的一个字符串,类定义于这个模块中。
-
__name__ 包含类名的一个字符串。
-
-
在C++和java等程序语言中,类可明确指出类的客户能访问哪些属性或方法。这些属性 或方法被认为是“公共”的。不能由类的客户访问的属性和方法则被认为是私有的。在python 中,对象的属性是肯定能访问的--没有办法阻止其它代码访问数据。然而,python提供一种 特别的机制来防止任意访问数据。在属性名附加双下划线前缀。python解释器会对属性执行 “名称重整”。如self.__hour,python会创建一个_Classname__hour的属性。但它一样是可 访问的,只是名字变了。
-
构造函数也可以定义默认参数,从而在客户没有指定参数的前提下,为对象属性指定初 始值。还可以定义关键字参数。
-
析构函数__del__是构造函数__init__的相反,用于执行“终止清理”,然后由解释器回收 对象的内存,使内存能被重用。析构函数通常只指定self参数,并返回None。
-
类的每个对象都拥有在构造函数中创建的所有属性的拷贝。特定情况下,类的所有对象 只能共享属性的一个拷贝。为此要使用“类属性”。它是“类范围”的信息(也就是说,它 是类的一个属性,而非类的特定对象属性)。它可节省空间和时间,提高性能。为访问类属 性,只需为属性名附加类名前缀,再加一个小数点即可。(Classname.xxx)
7.2. 良好的编程习惯
-
文档化字符串习惯上是一个三引号字符串。这样可以在不改变引号样式的前提下,扩展 一个程序的文档(例如添加更多的行)。
-
尽可包含文档化字符串,使程序更有条理。
-
将所有方法的第一个参数都命名为self。始终遵循这一命名约定,可确保不同程序员编 写的python程序是一致的。
-
属性名以单下划线开头,虽然在python语法中没有特殊的含义,但单下划线是python 程序员使用类时约定使用的符号,表明程序员不希望类的用户直接访问属性。以单划线开头 的属性揭示一个类的接口的相关信息。类如果定义了此类属性,它的客户就只能通过类提供 的访问方法来访问并修改属性值。如果不是这样做,通常会导致程序执行期间出现不可预料 的错误。
7.3. 常见编程错误
-
忘记将对象引用(通常是self参数)设为方法定义中的第一个参数,会导致该方法在运 行时被调用时,造成严重逻辑错误。
-
直接访问对象的属性可能导致数据进入不一致状态。一个办法是让类提供“访问方法”, 通过一种得到精心控制的方式来读写类数据。
-
如果忘记在方法内部通过对象引用(通常称为self)来访问由对象的类定义的另一个方 法,就会导致严重的运行时错误或者逻辑错误。如全局命名空间包含的一个函数与类的某个 方法同名,就会产生逻辑错误。此时,如果忘记通过对象引用来访问方法名,实际会调用全 局函数。
7.4. 测试和调试提示
-
即使提供了访问方法,也无法自动确保数据完整性,程序员必须提供有效性验证。
7.5. 软件工程知识
-
本书的中心思想是“重用,重用,再重用”。我们的重点是“创建宝贵的类”,创造有价 值的“软件资产”。
-
先初始化对象,再让客户代码调用对象的方法。不能依赖客户代码正确初始化对象。
-
利用访问方法控制对属性的访问(尤其是写访问)有助于确保数据完整性。
-
python的类和模块化机利于程序的独立实现。如果代码所用的一个类的实现发生了改变, 这段代码是无需更改的。
-
并不是所有的方法都要作为类的接口的一部份。有的方法是类的其它方法的一种实用方 法,不准备供类的客户使用。
-
将客户不应该访问的任何数据设为私有。
-
如果类的一个方法提供了构造函数(或其他方法)需要的全部或部份功能,请从构造函 数(或其他方法)中调用那个方法。这样可简化代码的维护,并减少代码的实现改变后出错 的可能。一个通用规则是:避免重复代码。
-
合成是软件重用的一种形式,即类的成员引用了其他类的对象。
-
在类成员引用了另一个类的对象的前提下,使那个成员对象能被公共访问,不但没有违 反封装性,而且还可隐藏那个成员对象的私有成员
7.6. 性能提示
-
链式比较表达式(0 <= a < 10)的效率比非链式表达式( a >= 0 and a < 10)更高,因为链式比较表达式中的每个条件都只执行一次。
-
如果数据的一个拷贝已经够用,请用类属性以节省空间。
Chapter 8. 自定义类
Table of Contents
8.1. 知识点8.2. 良好的编程习惯8.3. 常见编程错误8.4. 软件工程知识8.5. 性能提示8.1. 知识点
-
运算符+在python中具有多种用途,比如整数加法和字符串连接。这就是运算符重载的 一个例子。它会在不同的背景下执行最恰当的运算。
-
python 类可定义特殊方法__str__,为类的对象提供一个不正式的(即人们更容易理解的)字符串表示。如果类的客户程序包含以下语句:print objectofclass ,那么python会调用对象的__str__方法,并输出那个方法所返回的字符串。如: print test 就会执行以下语句: print test.__str__
-
前面介绍客户访问对象属性的方法有两种,一种是客户可直接访问属性(使用点访问运 算符):另外,也可通过客户定义的访问方法来访问属性。这一节讨论另外一种技术---定义 特殊方法,自定义直接属性访问的行为。python提供了一系列特殊方法,类可定义这此方法, 以控制点访问运算符操纵类对象的方式。如:
-
__delattr__ 客户删除一个属性时执行(例如 del anObject.attribute)
-
__getattr__ 客户访问一个属性名,但在对象__dict__属性中找不到这个名称时执行(例如anObject.unfoundName)
-
__setattr__ 客户将值指派给对象的属性时执行(例如 anObject.attribute = value)
-
-
在多数python运算符和增量赋值符号都能重载,有两个运算符不能重载,即{}和lambda。
-
如果重载一元运算符(如 + - * ),会自动重载与运算符对应的增量赋值语句。
8.2. 良好的编程习惯
-
如有必要,请为你创建的模块提供test函数,这些函数可确保模块正常工作,而且能通 过演示模块的工作方式,向客户提供额外的信息。如以下语句:
if __name__ == "__main__":
test()如果另一个程序导入模块,__name__的值就会是模块名,而test函数不会执行。如果模块 作为单独的程序执行,__name__的值是“__main__”,test函数就会执行。
8.3. 常见编程错误
-
从__str__方法返回非字符串值是严重的运行时错误。
-
在__setattr__方法中,通过点访问运算符为对象属性指派值会造成无穷递归。相反,应 使用对象的__dic__属性。见chapter8.2.py
8.4. 软件工程知识
-
对于大型系统,如果需要严格的数据访问,设计者就应使用__getattr__和__setattr__来确 保数据的完整性。大型系统的开发者如果使用python2.2,可借助于Properties这种更高效技 术来利用__getattr__ 和 __setattr__。
8.5. 性能提示
-
有时,更好的方法是重载运算符的增量赋值版本,以便能够“当场”执行操作(也就是 说,不通过新建对象来占用额外的内存)。
Chapter 9. 面向对象编程:继承
Table of Contents
9.1. 知识点9.2. 常见编程错误9.3. 软件工程知识9.4. 性能提示9.1. 知识点
-
创建新类时,程序员不必编写全新的属性和方法,只需指明新类继承以前定义好的“基 类”的属性和方法。新类称为“派生类”。每个派生类本身也可以是未来一些派生类的基类。 在“单一继承”中,类只从一个基类派生;但在“多重继承”中,派生类要从多个基类继承。
-
覆盖后的派生类构造函数通常会调用基类构造函数,从而先初始化基类属性,再初始化 派生类属性。
-
派生类可覆盖一个基类方法,做法是采取相同的名称提供那个方法的一个新版本。
-
通过继承,可自定义现有软件。首先继承现有类的属性和行为,再添加新的属性或行为, 或覆盖基类行为,从而对类进行自定义,使之符合我们的要求。
-
基类指的是公共特性---从基类继承的所有类者得到了基类的功能。在面向对象设计过程 中,设计者要找出公共特性,并对其进行归纳,以构成合理的基类。然后,派生类自定义基 类功能之外的功能。
-
绑定方法调用是通过一个对象来访问方法名,如anObject.method()。非绑定方法调用需 要通过类名来访问方法,并专门传递一个对象引用。如Point.__init__(self,x,y),self(Cricle 类的一个对象)作为对象引用传递。
-
我们在把类想像成一种类型时,就假定要创建那个类型的对象。但是,偶然也需要定义 一些类(程序员永远不打算创建它的任何对象)。这样的类称为“抽象类”。由于它们在继承 层次结构中作为基类使用,所以通常把它们称为“抽象基类”。我们不为抽象类创建对象。 它惟一的用途是提供一个合适的基类,以便其他类从中继承接口,偶尔也继承它的实现,从 中实际创建的类称为“具体类”。
-
python支持“多态性”,通过继承联系在一起的各个不同类的对象可针对同样的消息(方 法调用)做出不同的响应。发送给多个类型的对象的相同的消息会呈现出“多种型态”--- 这正是“多态性”一词的来历。例如,假定矩形类从四边形类派生,那么矩形属于四边形的 一个更具体的版本,能对四边形类的一个对象执行的操作(比台计算周长或面积),也能对 矩形类的一个对象执行。
-
现在讨论一下多态性的应用。一个屏幕管理器需要显示不同类的大量对象,其中包括软 件写好之后再加入系统的新类型。系统要能显示各种几何形状(基类是shape),例如正方形, 圆形,三角形,矩形,点,线等等(都从基类shape类派生)。屏幕管理器使用基类引用来 管理所有需要显示的类。绘制任何对象时(无论它位于继承层次结构的哪一级),屏幕管理 器都只是向对象发送一条draw消息。draw方法在每个派生类中都能被覆盖。shape类的每 个对象都知道怎样绘制自已。屏幕管理器不需要关心每个对象的类型,也不需要关心以前是 否见过这一种类型的对象---它只需要告诉每个对象draw自已就可以了。
-
多态性尤其适合实现分层式软件系统。例如在操作系统中,每类物理设备的工作方式 都是不同的,但无论如何,从设备“读”和“写”数据的命令肯定能统一。发送给设备驱动 程序对象的“写”消息需要在那个设备驱动程序的背景下进行专门解释,这具体取决于设备 驱动程序怎样操纵特定类型的设备。但是,就“写”调用本身来说,它和向系统中其它任何 设备的“写入”操作没有任何区别---都是将一些数量的字节从内存中放到设备中。面向对象 的操作系统可使用一个抽象基类提供适用于所有设备驱动程序的一个接口。然后,通过从这 个抽象类继承,派生类可采取类似的方式工作。设备驱动程序具有的功能(即接口)作为抽 象基类中的方法提供。在派生类中,对这些方法进行了具体的实现,它们与特定类型的设备 驱动程序是相对应的。
-
通过多态性编程,程序可遍历一个容器,比如由类层次结构各个级别上的对象构成的 一个列表。只需发关一条消息就能执行列表中的所有对象。
-
在python2.2之前的版本中,类和类型是两种截然不同的编程元素。所以程序员不能从 内建类型继承,不便使用列表,字典和其它对象提供的高级数据处理能力。自python2.2起, 类的本质与行为都发生了变化,消除了类型和类的差异。在将来的所有2.X版本中,程序员 可区分两种不同的类,即所谓的“经典类”(它的行为方式与本章前面以及前两章所展示的 类相同)以及“新类”(它们具有新的行为)。object类型用于定义新类型。直接或间接继承 于object的所有类都具有为新类定义的行为。
-
所有类都可定义“静态方法”。静态方法可由一个客户调用,即使不存在类的任何对象。 通常,静态方法是类的一个实用方法,不需要类的一个对象就能执行。一个类如果想把方法 指定为静态的,就必须向内建函数staticmethod传递方法的名称。再为函数调用返回的值绑 定一个名称。静态方法不将self指定为第一个参数。这样一来,即使没有类的对象,也能调 用静态方法。
-
静态方法在java等语言中至关重要,这些语言要求程序员将所有代码都放入一个类定 义中。使用这些语言,程序员经常要定义只包含表态实用方法的类。随后,类的客户可调用 静态方法,这和python程序调用模块里定义的函数的方式非常相似。在python中,静态方 法允许程序员更准确地定义一个类接口。如果类的方法不需要类的对象即可执行其的任务, 程序员就可将这个方法指定为静态的。
-
从基类object继承的类也可定义__getattribute__方法,每次访问属性时都会执行它。 派生类中的__getattribute__方法必须调用方法的基类版本,才能获取属性值,这是由于假 如通过对象的__dict__来访问属性值,会造成对__getattribute__的另一次调用。
-
python2.2允许新类定义一个__slots__属性,它可列出只允许类的对象拥有的属性。
9.2. 常见编程错误
-
如果派生类的已覆盖构造函数需要调用基类构造函数来初始化基类的成员,派生类构造 函数就必须显式地调用基类构造函数。不从派生类中调用基类构造函数是逻辑错误。
-
基类方法在派生类中被覆盖后,经常要让派生类版本调用基类版本,并执行一些附加的 操作。在这种情况下,如果不使用基类名称来引用基类方法(也就是为基类方法附加基类名 称和一个点前缀),会造成无穷递归,因为派生类方法实际调用的是自身。这最终导致系统 死机。
-
不将非绑定方法调用的第一个参数指定为对象引用是逻辑错误。
-
要保证正确的属性访问,__getattribute__方法的一个派生类版本应该调用该方法的基类 版本。如试图通过访问对象的__dict__来返回属性值,会造成无穷递归。
9.3. 软件工程知识
-
和其他任何类一样,派生类不一定要定义构造函数。如果派生类没有定义构造函数,一 旦客户创建类的一个对象,就会创建类的基类构造函数。
-
创建派生类不会影响其基类源代码;基类的完整性通过继承获得了保持。
-
在面向对象系统中,类通常是紧密联系的。因此,请归纳出公共属性和行为,将其放入 一个基类,然后通过继承来生成派生类。
-
修改基类时,只要到基类的接口保持不变,就不必修改派生类。
-
通过多态性,程序员可关注于公共特性,让执行时环境去关心具体特性。程序员可在不 知道那些对象是什么类型的情况下,指示大量对象采取恰当的行动。
-
多态性增强了扩展性:软件如果要调用多态行为,编写时便不用关心要向其发送消息的 对象类型。因此可在不修改基本系统的前提下,自由添加新类型对象,令其响应现有的消息。
-
抽象类为类层次结构的各个成员定义了一个接口。抽象类包含的方法将在派生类中定 义。在多态性的帮助下,层次结构中的所有方法都可使用同一个接口。
-
如果新类定义了__slots__属性,但类的构造函数没有初始化属性值,那么一旦创建该类 的一个对象,python就会将None值指派给__slots__中的每个属性。
-
派生类会继承它的基类的__slots__属性。然而,如果不希望程序为派生类的对象添加属 性,派生类就必须定义自已的__slots__属性。在派生类的__slots__中,只包含允许的派生类 属性名,但客户仍可为派生类的直接/间接基类所指定的属性赋值。
9.4. 性能提示
-
如果通过继承生成的类超过所需,可能会无谓地浪费内存和处理器资源。因此,请从最 能满足你需求的类继承。
Chapter 10. 图形用户界面组件(一)
Table of Contents
10.1. 知识点10.2. 良好的编程习惯10.3. 常见编程错误10.4. 界面知识10.1. 知识点
-
GUI是事件驱动的,也就是说,一旦用户与GUI交互,GUI组件就会生成”事件“(动 作)。常见交互包括移动鼠标、单击鼠标按钮、在文字段输入、从菜单选择一个选项以及关 闭一个窗口等等。
-
Tkinter模块中的每一个Tk GUI组件都是从Widget类继承的一个类。所有从Widget派 生的类都具有公共的属性及行为。
-
Entry(输入)组件是一种特殊的屏幕区域,用户可以在其中输入文本,也可显示一行 文本。
-
pack方法规定了组件应该如何放到它的“父”中,以及应该放到什么地方。
-
如果程序员不指定名称,Tkinter会为每个组件分配一个不重复的名称。要想获得一个组 件的完整名称,将组件对象传给str函数即可。
-
bind方法将一个<Return>事件同text1组件关联在一起。一旦用户按下回车键,<Return> 事件就会发生。bind方法要取得两个参数,一个是事件类型(事件格式),一个是要与事件绑定的 那个方法的名称。
-
insert方法在Entry组件中写入文本,它要取得两个参数:文本插入的位置和包含插入文本 的一个字符串。如果第一个参数是INSERT,文本就会在当前光标位置插入。如果是END,则在末尾插 入,如:insert(END,text)。不可用delete(start,finish)方法从Entry组件中删除文本。在 Entry组件中,第一个位置编号是0,所以如果delete(0,END)则是删除所有文本。
-
Widget 的winfo_name方法返回组件名,Entry的get方法返回Entry的内容。showinfo() 函数显示一个标记为"message"的对话框。
-
Button的command关键字参数指定了在用户选择按钮之后要执行的事件处理程序。
-
pack的参数,side指明组件要靠在容器的哪一边,可选择TOP(DEFAULT),BOTTOM,LEFT 和 RIGHT。fill指定组件在容器中应占据的空间(在指定的方向上),可设为NONE(DEFAULT),X,Y 或 BOTH。expand设置组件只否自动扩展,填充容器中任何额外的空间。padx and pady可围绕组件插 入空白填充。pack_forget方法可从容器中删除一个pack好的组件。
-
Grid布局管理器将容器分割为一个网格,使组件能以行、列形式放置。组件在网格中的位置 由其row和column值决定;网格中的每个单元格都可包含一个组件。行、列编号从0开始。还可以调 用rowconfigure和columnconfigure方法来设置行和列。
-
place布局管理器允许设置一个GUI组件的位置和大小---既可以是绝对值,也可是与另一个 组件的相对位置和大小。它较复杂在这里不详细讨论。
10.2. 良好的编程习惯
-
为每个Button都单独定义一个回调方法,这有助于避免混淆,确保实现目标行为,并简化GUI 的调试。
-
妥善选择布局管理器可使GUI编程更容易。编程之前,先画好设计图,再选择最合适的布局管理 器。可用的布局管理器有三种,Pack(按添加的顺序放置组件),Grid(以行、列形式排列组件), Place(允许程序员指定组件和窗口的大小和位置)。
10.3. 常见编程错误
-
在同一个容器中使用多种类型的布局管理器,一旦Tkinter试图协调不同管理器的不同需求,就 会导致应用程序死机。
-
pack方法按定义顺序将各组件放到一个容器中,所以,如果错误定义顺序,会导致不可预料的结果, 相反,如果放置组件时,采用了为side,expand,fill,padx,pady指派值的方式,就可以忽略其定义顺 序,保证获得所需结果。
-
有时可能指定重叠组件。代码中先出现的组件会被最近添加的组件遮住。
10.4. 界面知识
-
通过改变按钮的显示效果,可提供一种视觉线索,让用户知道自已已经选中了按钮,而且发生了 相应的动作。
Chapter 11. 图形用户界面组件(二)
Table of Contents
11.1. 知识点11.2. 测试和调试提示11.3. 界面知识11.1. 知识点
-
python巨元件(Python Megawidgets,Pmw)提供高级的GUI组件,它是基于Tkinter模块提供 的较少的组件开发的。每个Pmw组件都合并了一个或多个Tkinter组件,从而生成更有用,更复杂的组件。
-
ScrolledListBox组件叫滚动列表框,是由于列表中的项目个数较多,以至于列表无法在屏幕上显 示时的解决方案。
-
ScrolledText组件其实是能滚动的Tkinter Text组件。
-
MenuBar组件用于创建菜单。
-
Canvas组件用于显示文本、图像、线条、和形状。
-
Scale(滑杆)组件允许用户从一系列整数值中选择。
-
其它GUI工具包有:PyGTK为Gimp TooKit组件提供了面向对象的接口。GTK是一个高级的组件集,主要 在X WINDOWS系统中使用。PyGTK是GTK+的一部份,后者也是一个Python工具包,用于创建图形用户界面。另 一个流行的GUI工具包是wxPython,它是一个Python扩展模块,提供了对wxWindows(一个用C++写成GUI库) 访问途径。PyOpenGL为OpenGL库提供了一个python编程接口。OpenGL是开发交互式二维和三维图形应用程序 的流行方案,
11.2. 测试和调试提示
-
程序使用Pmw时,如果不调用Pmw.initialise,就不能使用Pmw模块的完整功能。
11.3. 界面知识
-
菜单项按照它们添加的顺序出现,因此,务必按正确顺序添加。通常按添加顺序从左到右排列。
Chapter 12. 异常处理
Table of Contents
12.1. 知识点12.2. 良好的编程习惯12.3. 常见编程错误12.4. 测试和调试提示12.5. 软件工程知识12.6. 性能提示12.1. 知识点
-
Python中异常处理的样式及细节基于Modula-3语言,类似于c#和java。
-
Python使用try语句实现异常处理。try语句包围着可能引发异常的语句。try语句以关键字try 开头,后续一个冒号和一个可能在其中引发异常的代码块。try可指定一个或多个except子句,它们紧接 在try块后,每个except子句指定了零个或多上异常类名,它们代表要由except子句处理的异常类型。 可在except子句中指定一个标识答,程序用它引用被捕获的异常对象。处理程序利用标识符从异常对象 获取与异常有关的信息。如果except子句中没有指定异常类型,则会捕捉所有类型的异常。在最后一个 except子句之后,可选择性地添加一个else子句。如果try块中的代码没有引发异常,就会执行else 子句中的代码。如果try语句中没有指定except子句,那不必须包含一个finally子句,该子句肯定会执 行,无论是否发生异常。
-
异常是一些类的对象,这些类都是从Exception类继承的。如果有一个try块中发生异常,这个块会 立即终止,程序控制权会移交给try块后的第一个except处理程序。解释器为了确定相匹配的except,需 要将引发的异常类型与每个except类型比较,直到匹配为止。如果类型完全一致,或引发的异常类型是 except处理程序的异常类型的一个派生类,就表明发生了匹配。
-
如果try块中没有发生异常,解释器将忽略用于try语句的异常处理程序,并执行try语句的else子句 (如果有的话),如果没有发生异常,或某个except子句处理了这个异常,程序会从try语句之后的下一个 语句恢复执行。如果某个语句发生了异常,但该语句不在try块中,而是在一个函数中,那么包含这个语句的 函数会立即终止,解释器会试图在(发出)调用(的)代码中查找一个封闭的try语句,这个过程称为“堆栈 辗转开解”。
-
python使用“异常处理的终止模型”,因为引发异常的try块会在异常发生后立即“过期”。有的语言 使用“异常处理的恢复模型”,完成异常处理后,控制权会返回异常引发点,并从那个位置恢复执行。
-
尽管可在try块中包含任意语句,但通常只包含可能引发异常的语句。在else块中,则放置不会引发异 常的、而且只有在相应的try块中没有发生异常的前提下才应该执行的语句。
-
python的Exception异常类是一种层次结构,从Exception继承的4个类包括SystemExit,StopIteration, Warning以及StandardError。
-
python中不存在内存泄漏问题,解释器会自动进行“垃圾回收”。但解释器不能完全清除内存泄漏。只 要存在对一个对象的引用,解释器就会对其进行垃圾回收。所以,如果程序员偶然保留了不需要的对象引用, 仍会出现内存泄漏。
-
python异常处理机制提供了finally子句,只要程序控制进入相应的try块,就必然会执行这个finally 子句(无论try成功执行与否),所以finally是放置资源回收代码的理想地点。如果try块执行成功,则finally 会在try块终止后立即执行,如果try块发生异常,则会在导致异常的那一行之后,立即执行finally子句。
12.2. 良好的编程习惯
-
使每类问题都与一个命名得体的异常类关联,使程序结构清晰易读。
-
在创建程序员自定义异常类之前,应分析python层次结构中现有有异常类,看是否有可满足自已需要的类, 只有在程序需捕捉和处理与现有异常类型不同的新异常时,才定义新异常类。
12.3. 常见编程错误
-
退出程序可能导致资源(比如文件或网络连接)无法由其它程序使用。这称为“资源泄漏”。
-
在try语句中同时包括except和finally子句是语法错误。可接受的组合形式中有:try/except, try/except/else以及try/finally。
-
每个try块与其第一个except处理程序之间、两个except处理程序之间、最后一个except处理程 序与else子句之间或者try块和finally子句之间,不允许出现任何语句,否则是语法错误。
-
如果try语句既不包括finally语句,也不包括except语句,那就属于语法错误。如果try语句不包括 except子句,那么必须包括一个finally语句,如果try语句不包括finally语句,那么至少包括一个except子句。
-
在finally中引发异常是非常危险的,执行finally子句时,如果一个未被捕获的异常正在等候处理,而 finally又引发一个新的、未被该块捕获的异常,那么第一个异常就会丢失,新异常则传递给下一个封闭的try语句。
12.4. 测试和调试提示
-
在finally块中,必须将可能引发异常的代码封闭到一个try语句中,这样可避免丢失未捕捉的、 在finally块执行之前发生的异常。
-
traceback展示了自异常发生之时起,一个完整的函数调用堆栈。这样一来,程序员就可查看导致 异常的一系列函数调用。traceback中的信息包括辗转开解的函数名称:在其中定义了函数的那个文件的 名称以及程序遇到错误时的行号。traceback中的最后一个行号是“引发点”(即初始异常引发的位置)。 在它之前的一系列行号则是在traceback中每个函数的调用位置。
-
阅读traceback报告时,请按从下到上的顺序,先读错误消息。然后,向上阅读traceback剩余部 份,查找报告中的第一个行号。通常,这就是导致异常的位置。
12.5. 软件工程知识
-
从设计阶段初期,就要将异常处理策略考虑到系统中。系统实现后再添加有效的异常处理是很难的。
-
过去,程序员用多种技术来实现错误处理代码。异常处理则提供了一种统一的机制来处理错误, 因此,当许多程序员从事同一个大型项目时,相互之间可轻松理解对方的错误处理代码。
-
通常不要将异常显式地应用于常规控制流程(虽然也可这样做),否则会因为较难跟踪随后发生 的大量异常,导致程序变得难以阅读和维护。
-
要精心选择放入try块中的代码,其中应有好几个语句都可能引发异常。尽量避免为可能引发异常 的每个语句都单独使用一个try语句。但是,要想确保正确的异常处理,每个try语句都应尽可能封装一 个足够小的代码区,这样一来,一旦发生异常,就可确切把握当时的背景,使except处理程序能正确地 处理异常。如果一个try块中的多个语句都可以引发相同的异常类型,就必须用多个try语句确定每个异 常的背景。
-
在一个except子句中,可以指定多个异常,只需在圆括号中使用由逗号分隔的一个异常名称序列 即可,要用except子句指定我个异常,这些异常应以某种形式相互关联(如都是因算术计算错误而引发 的异常)。将相关的异常分为一组,再为每一组单独使用一个except子句。
-
引发异常的代码要先释放代码中获取的任何资源,再引发异常。
-
如果try语句指定了一个finally子句,那么即使try块由一个return语句终止,finally子句的 代码也会。执行完成后,才轮到通过return返回调用代码。
12.6. 性能提示
-
如果没有发生异常,异常处理代码对性能的影响极微(或者说根本没有),所以,相较于在程序逻 辑中混合了错误处理逻辑的程序,实现了异常处理的程序效率更高。
Chapter 13. 字符串处理和正则表达式
Table of Contents
13.1. 知识点13.2. 良好的编程习惯13.3. 性能提示13.1. 知识点
-
python提供了find 和 index等方法来查找字符串。
-
字符串的count方法返回子字符串在字符串或字符串分片中出现的次数,如果方法没有找到指定的子字符 串,方法返回0。
-
find方法返回子字符串出现时的最低索引位置。如果找不到,则返回-1.index方法类似于find方法。只 是假如字符串中不包含子字符串,方法会引发ValueErro异常。这样,程序就可以捕捉该异常,并进行处理。
-
startswith方法用于判断一个字符串是否以指定子字符串开始,如果是则返回1。
-
endswith方法用于判断一个字符串是否以指定子字符串结尾,如果是则返回1。
-
rfind方法从字符串尾部开始搜索,返回首次出现子字符串的索引位置。如果没有发现则返回-1。
-
rindex方法则返回字符串出现的最高索引位置,没有发现子串就引发ValueError异常。我们的程序就可 捕捉该异常,以处理字符串中不包含指定字符串的情况。
-
replace方法可取得两个子字符串,并在文档中搜索第一个字符串,把它替换成第二个字符串。它还可取得 第三个参数,指定最大的替换次数。如只允许替换3次,其它的忽略。
-
split和join方法用于分解和连接字符串。
-
所谓正则表达式,是一种"文本模式"(Text Pattern),用于查找与模式相匹配的子字符串。
-
原始字符串(Raw String)是在字符串之前加上字符前缀r后创建的一个字符串,在原始字符串中,Python 不会把反斜杠\视为转义字符,而是理解为一个\符号。
-
re.sub函数取得3个参数,第一个指定模式,第二个指定子字符串,第三个指定字符串。在第三个参数指定的 字符串中,与模式匹配的每个子字符串都会被替换成第二个参数指定的子字符串。
-
re.split函数取得两个参数,第一个参数是正则表达式,用模式来描述定界符。函数在定界符处对第二个参数 进行分解,返回一个标记列表。
13.2. 良好的编程习惯
-
如果进行简单的处理,请使用字符串方法。这样可避免复杂的正则表达式所带来的错误,并保证程序的可读性。
-
正则表达式的模式字符串中常常包含反斜杠字符。使用原始字符串来创建模式,可避免对其中每个反斜杠进行 转义的必要,使模式字符串更容易理解。
13.3. 性能提示
-
构建复杂字符串时,效率更高的一种做法是将不同组件包括到一个列表中,再用join方法汇总字符串,而不 要使用连接运算符(+)。
Chapter 14. 文件处理和序列化
Table of Contents
14.1. 知识点14.2. 良好编程习惯14.3. 常见编程错误14.4. 性能提示14.1. 知识点
-
python程序开始执行时,会创建3个文件流,包括sys.stdin(标准输入流),sys.stdout(标准输出流), 以及sys.stderr(标准错误流)。这些流在程序和特定文件/设备之间建立沟通渠道。
-
文件打开模式指出文件打开后要进行读取,写入,还是两者兼而有之。各种模式说明如下:
a所有输出都写到文件尾,如果指定的文件不存在,就创建一个。
r打开文件以便输入。如果文件不存在,就引发IOError异常。
r+打开文件以便输入和输出。如果文件不存在,就引发IOError异常。
w打开文件以便输出。如果文件存在,就删除其中所有数据。如果不存在就创建一个。
w+打开文件以便输入和输出。如果文件存在,就删除其中所有数据。如果不存在就创建一个。 不有一中是以上所有模式后加b,如ab,rb,表示打开二进制文件(也就是非文本形式)输入或输出,注意,只是windows和macintosh平台支持这些模式。
-
options[]()结构表示,把()里的参数传给[]里的函数处理。这样的结构可避免使用冗长的if/else语句来判断用户菜单选项。
-
序列化(Serialization)是指将用户自定义类等复杂对象类型转换成字节集,以便存储或通过网络传输。序列化也称为“平坦化”(Flattening),或者“编组”(Marshalling),python用pickle和cPickle模块来执行序列化。
14.2. 良好编程习惯
-
如果不希望文件内容被修改,应以只读模式打开文件(使用"r"),防止因为不慎而修改文件内容。这是"最小权 限原则”的一个例子。
-
python2.2 中允许程序员在for语句中使用文件对象。如for record in file: ,这样可每次读取file的一行,并将该行指派给recode。程序可立即处理那一行。与通过readlines方法读取一个大文件的内容相比,用这种方 法遍历文件中的各行显得更高效。因为使用readlines,必须先将整个文件都读入内存,然后才能对文件内容进行处理。
-
文 件对象提供seek方法,可用它重新定位“文件位置指针”,如file.seek(0,0),会将“文件位置指针”重新定 位至文件头。seek的第一个参数是“偏移量”,它是一个整数值,以字节数的形式指定了文件中的一个位置(要根据文件的“seek方向”。第二个参数是可 选的,指定偏移的开始位置,或称"seek方向"。seek方向可设为0,表明相对于文件头进行定位;也可设为1,相对于当前位置进行定位;或设为2,表 明相对于文件尾进行定位。
-
python提供shelve模块模拟随机访问文件。shelve对象具有一个字典接口--也就是访问记录信息的记录键。
14.3. 常见编程错误
-
在用户希望保留原始文件内容的前提下,用"w"模式打开文件以进行输出,是一个逻辑错误,因为文件内容会在用 户未得到警告的情况下删除。
14.4. 性能提示
-
一旦程序不再需要引用文件,就显式地关闭文件,这样可节省程序所用的资源,还有助于改善程序的可读性。
-
cPackle模块的执行效率高于pickle模块,因为cPickle是用c实现的,并被编译成每种平台上的原生机器语言。