3.6 第一个Python程序

我们已经熟悉了语法、代码风格、变量赋值及内存分配,现在来看一点略微复杂的代码。这个例子中还有你不熟悉(我们还未讲到的)的Python结构,不过我们相信因为Python非常的简单和优雅,你一定可以弄懂每一行代码的用途。

我们将要介绍两段处理文本文件的相关脚本。首先是makeTextFile.py,创建一个文本文件;它提示用户输入每一行文本,然后将结果写到文件中。另一个是readTextFile.py,读取并显示该文本文件的内容。研究一下这两段代码,看看他们是如何工作的。

例3.1 创建文件(makeTextFile.py)

这个脚本提醒用户输入一个(尚不存在的)文件名,然后由用户输入该文件的每一行。最后,将所有文本写入文本文件。

3.6 第一个Python程序 - 图1

1 ~ 3行

UNIX启动行之后是模块的文档字符串。应该坚持写简洁并有用的文档字符串。这里我们写的有点短,不过对这段代码已经够用了。(建议读者看一下标准库中cgi模块的文档字符串,那是一个很好的示例)。

5 ~ 6行

之后我们导入os模块,在第6行我们为os.linesep属性取了一个新别名。这样做一方面可以缩短变量名,另一方面也能改善访问该变量的性能。

3.6 第一个Python程序 - 图2核心提示:使用局部变量替换模块变量

类似os.linesep这样的名字需要解释器做两次查询:(1)查找os以确认它是一个模块, (2)在这个模块中查找linesep变量。&为模块也是全局变量,我们多消耗了系统资源。如果你在一个函数中像这样频繁使用一个属性,我们建议你为该属性取一个本地变量别名。变量查找速度将会快很多——在查找全局变量之前,总是先查找本地变量。这也是一个让你的程序跑的更快的技巧:将经常用到的模块属性替换为一个本地引用。代码 “跑”得更快,而也不用老是敲那么长的变量名了。在我们的代码片段中,并没有定义函数,所以不能给你定义本地别名的示例。不过我们有一个全局别名,至少也减少了一次名字查询。

8 ~ 4行

显然这是一个无限循环,也就是说除非我们在while语句体中提供break语句,否则它会一直循环下去。

while语句根据后面的表达式决定是否进行下一次循环,而True则确保它一直循环下去。

10 ~ 14行

提示用户输入一个未使用的文件名。raw_input()内建函数接受一个“提示字符串”参数,作为对用户的提示信息。raw_input()返回用户输入的字符串,也就是为fname赋值。如果用户不小心输入了一个已经存在的文件的¿字,我们要提示这个用户重新输入另一个名字。os.path.exists()是os模块中一个有用的函数,帮助我们确认这一点。当有输入一个不存在的文件名时,os.path.exists()才会返回False,这时我们中断循环继续下面的代码。

16 ~ 26行

这部分代码提供用户指令,引导用户输入文件内容,一次一行。我们在第17行初始化了列表all,它用来保存每一行文本。第21行开始另一个无限循环,提示用户输入每一行文本,一行仅输入一个句点 (.)表示输入结束。23~26行的if-else语句判断是否满足结束条件以中止循环(24行),否则就再添加新的一行(26行)。

28 ~ 32行

现在所有内容都保存在内存当中,我们需要将它们保存到文件。第29行打开文件准备进行写操作,第30行将内存中的内容逐行写入文件。每个文件都需要一个行结束符(或文件结束字符)。第30行的结构称为列表解析,它进行以下工作:对我们文件的每一行,根据程序运行平台添加一个合适的行结束符。 ‘%s%s’为每一行添加行结束符,(x,ls)表示每一行及其行结束符,对Unix平台,是‘\n’,对DOS或win32平台,则是‘\r\n’。通过使用os.lineseq,我们不必关心程序运行在什么平台,也不必要根据不同的平台决定使用哪种行结束符。文件对象的writelines()方法接收包含行结束符的结果列表,并将它写入文件。

不错吧。现在来看一下如何查看刚刚创建的文件。出于这个目的,我们创建了第二个Python脚本, readTextFile.py。你会看到,它比makeTextFile.py短的多。创建一个文件的复杂度总是比读取它要大。你可能感兴趣的、有新意的一点在于异常处理的出现。

1 ~ 3行

和前面一样,是Unix启动行及模块文档字符串。

5 ~ 7行

不同于makeTextFil.py,我们在这个例子中不再关心用户是否输入合适的文件名。

例3.2 文件读取和显示(readTextFile.py)

3.6 第一个Python程序 - 图3

3.6 第一个Python程序 - 图4

换句话说,我们在其他地方进行验证工作(如果需要)。第7行打印一个空行,以便将提示信息和文件内容分隔开来。

9 ~ 18行

脚本的剩余部分展示了一种新的Python结构,try-except-else语句。try子句是一段我们希望监测错误的代码块。在第10〜11行代码,我们尝试打开用户输入的文件。except子句是我们处理错误的地方。在12〜13行,我们检查open()是否失败——通常是IOError类型的错误。

最后,14〜18行的else子句在try代码块运行无误时执行。我们在这儿将文件的每一行显示在屏幕上。注意由于我们没有移除代表每行结束的行结束符,我们不得不抵制print语句自动生成的行结束符——通过在print语句的最后加一个逗号可以达到这一目的。第18行关闭文件,从而结束这段脚本。

最后要讲的一点是关于使用os.path.exists()和异常处理:一般程序员倾向于使用前者,因为有一个现成的函数可以检查错误条件——并且很简单,这是个布尔函数,它会告你“是”还是“不是”(注意,这个函数内可能已经有异常处理代码)。那你为什么还要重新发明一个轮子来干同样一件事?异常处理最适用的场合,是在没有合适的函数处理异常状况的时候。这时程序员必须识别这些非正常的错误,并做出相应处理。对我们的例子来说,我们能够通过检查文件是否存在来避免异常发生,不过因为有可能因为其他原因造成文件打开失败,比如缺少权限,网络驱动器突然连接失败等等。从更安全的角度来说,就不应该使用类似os.path.exists()之类的函数,而是使用异常处理,尤其是在没有合适函数的情况下更应如此。

你会在第9章中找到更多文件系统函数的例子,在第10章则有更多关于异常处理的知识。