Python 之旅 ()

/马儿 (marr@linux.org.tw)

认识函式与模组之后,Python 的学习之路显得宽广许多,本期的重点将从字串延伸至档案的处理上,包括档案系统与资料 I/O 两大部份,其学习经验又与作业系统环境具有密切关系,最后并简介例外处理的范例。

几个有用的函式

为了方便接续的内容,我们先来认识一个实用的模组,称为 string,顾名思义,可用于协助处理字串物件。

>>> import string

>>> date = "Fri May 18 CST 2001"

>>> piece1 = string.split(date)

>>> piece1

['Fri', 'May', '18', 'CST', '2001']

>>> time = "12:03:27"

>>> piece2 = string.split(time, ':')

>>> piece2

['12', '03', '27']

>>> string.digits

'0123456789'

由上述范例,可以得知模组 string 里有个 split() 的物件方法,其工作原理是将一个字串变数值,依空白字元 (预设状况) 为切割点,分解成数个小字串,形成一个字串串列传回。如果切割条件不是空白字元时,在 split() 所接参数中予以指定,如 split(time, ':') 就是指定要以 ':' 字元为切割点。范例的最后一行,则是显示模组 string 有个字串变数 digits,其内容设定为 '0123456789'

如果我们想把上述字串串列里的‘数字’,如 '18' '2001',由字串型别转换成数值型别,可以怎么做呢? 下列是个方法。

>>> def try_ai(s):

... if s[0] in string.digits:

... return string.atoi(s)

... else:

... return s

...

>>> import string

>>> date = "Fri May 18 CST 2001"

>>> piece = string.split(date)

>>> finish_ai = map(try_ai, piece)

>>> print finish_ai

['Fri', 'May', 18, 'CST', 2001]

>>>

首先,定义一个叫做 try_ai() 的函式,它在读进字串后,会比对字串的第一个字元,如果第一个字元是属于阿拉伯数字,那么就会尝试透过 string 模组的 atoi() 物件方法,将字串转换成整数,最后传回其整数型别资料。是的,你会发现它的演算规则有些天真,不过,我们暂时还不需要一个无懈可击的转换函式。

我们载入模组 string 之后,利用内建函式 map() 将自制函式 try_ai 与字串串列 piece 连结起来。map() 接受两个参数,一个是函式,一个是序列资料,它会依序读取序列资料的元素内容,加工后传回,如此一来,便能如愿将 piece 里的部份字串,转换成数值型别,做法上显得相当简洁。

接下来,我们就可以进一步稍微改良原本天真的 try_ai() 函式。

>>> def try_ai(s):

... if ':' in s:

... ts = string.split(s, ':')

... return map(string.atoi,ts)

... if s[0] in string.digits:

... return string.atoi(s)

... else:

... return s

...

>>> import string

>>> date = "Fri May 18 12:03:27 CST 2001"

>>> piece = string.split(date)

>>> finish_ai = map(try_ai, piece)

>>> print finish_ai

['Fri', 'May', 18, [12, 3, 27], 'CST', 2001]

>>>

这个改良过的版本,可以进一步处理像 '12:03:27' 这样的‘数字’,否则原本更天真的版本会传回 ValueError 的错误讯息。

>>> piece = ['Fri', 'May', '18', '12:03:24', 'CST', '2001']

>>> def strp(x, y):

... return x + ' ' + y

...

>>> r = reduce(strp, piece)

>>> r

'Fri May 18 12:03:24 CST 2001'

>>>

上述程式片段,处理效果刚好与之前的程式相反,它会把字串串列重组成一个长字串。重点就是利用了内建函式 reduce(),其运作方式同样要输入一个函式名称及一个序数资料,不过,目的是要把序数资料的元素‘合并减少’成一个。reduce() 也可以应用在数值串列上,以下便是这样的范例。

>>> n = range(1, 11)

>>> def mult(x, y):

... return x * y

...

>>> f = reduce(mult, n)

>>> f

3628800

>>>

说穿了,它还是一个阶乘的范例,每呼叫一次 mult() 函式,数值串列的个数会越来越少,最后传回一个阶乘结果,在此例中,即 10! 的答案。

下列是一个简化版本的闰年判断程式,我们将引入另一个函式 filter()

>>> def leap(y):

... if (y%400) == 0:

... return 1

... elif (y%100) == 0:

... return 0

... elif (y%4) == 0:

... return 1

... return 0

...

>>> n = range(1900, 2001)

>>> leap_year = filter(leap, n)

>>> leap_year

[1904, 1908, 1912, 1916, 1920, 1924, 1928, 1932, 1936, 1940, 1944, 1948, 1952, 1956, 1960, 1964, 1968, 1972, 1976, 1980, 1984, 1988, 1992, 1996, 2000]

>>>

函式 filter() 同样是接一个函式名称与一个序数资料为参数,重点在于,在自制函式中,你必须把‘想留下来的资料’,其函式传回值设为 1 (代表 true),而把‘不想留下来的资料’,其函式传回值设为 0 (代表 false)。如此一来,filter() 函式在依序处理完序数资料后,还是会传回一个序数资料,而且应该只留下你想要的资料。

lambda 表示式

lambda 表示式是个实用而重要的工具,其功能及本质还是函式,差别在于 lambda 没有‘明确的函式名称’,而且通常只用于简洁的函式叙述。

>>> n = range(1, 11)

>>> f = reduce(lambda x, y: x * y, n)

>>> f

3628800

>>>

又是熟悉的阶乘函式,有趣的是,它的表示方式非常简洁,乍看之下,初学者可能以为是天书了。lambda 表示式的语法如下:

lambda 参数串列: 表示式

例如 x + yx * ys[9] 都是表示式的例子。早期,lambda 表示式的概念是取自 Lisp 语言,使用上有其便利及优势,不过,对初学者而言,使用 lambda 表示式通常还是得花时间熟悉,若是‘画虎不成反类犬’,搞到程式大乱就得不偿失了。

读取档案系统 filesystem 资讯

档案系统的相关工作,大抵围绕着档案、目录为主题,下列的基本功能应用场合极广。

>>> import os Œ

>>> os.getcwd() 

'/home/marr'

>>> os.listdir(os.curdir) Ž

['.bash_history', '.bash_logout', '.bash_profile', '.bashrc', '.muttrc', 'signature', '.ssh', '.viminfo', '.vimrc']

>>> os.chdir('/tmp') 

>>> os.path.join('usr', 'local', 'bin') 

'usr/local/bin'

>>> mount_point = '/mnt/cdrom'

>>> rpm_dir = 'Mandrake/RPMS'

>>> complete_dir = os.path.join(mount_point, rpm_dir) 

>>> print complete_dir

'/mnt/cdrom/Mandrake/RPMS'

>>> os.path.split(complete_dir)

('/mnt/cdrom/Mandrake', 'RPMS')

>>> os.path.basename(os.path.join(complete_dir, 'some.rpm'))

'some.rpm'

>>> os.path.dirname(os.path.join(complete_dir, 'some.rpm'))

'/mnt/cdrom/Mandrake/RPMS'

>>> os.path.splitext(os.path.join(complete_dir, 'some.rpm'))

('/mnt/cdrom/Mandrake/RPMS/some', '.rpm')

>>> os.path.commonprefix(['/usr/bin', '/usr/src'])

'/usr/'

>>> os.path.expandvars('$HOME/temp')

'/home/marr/temp'

>>>

Œ 目录、路径的相关处理,与 os 模组有关,操作前要记得将之 import

 getcwd() 传回现行工作目录资讯。

Ž 利用 listdir() 可以将目录内之档案资讯列出,使用者必须提供‘目录字串’为输入参数,如 os.curdir 指的便是‘现行工作目录’字串,而传回值为一字串串列,即目录下档案名称所集的串列。

 利用 chdir() 可以进行目录更换的动作,同样是提供‘目录字串’为输入参数。

 此处显示的是 Linux 环境下的执行结果,如果是 DOS/Windows环境,其执行结果会像是 usr\local\bin

输入一个档案字串,os.path.split() 会传回一个值组,其元素内容分别为目录名称与最后的档案名称。

输入一个档案字串,os.path.basename() 会传回一个字串,为路径资讯中最后的档案名称, os.path.dirname() 会传回一个字串,为路径资讯中前面的目录名称。

输入一个档案字串,os.pah.splitext() 可以传回一组值组,其元素内容分别是档案名称的前部份,以及最后的延伸档名。

输入一组路径字串串列,os.path.commonprefix() 可以协助找出最小的共同路径值。

透过 os.path.expandvars() 可以应用类似 $HOME 的环境变数。

系统档案处理的必备工具

下列的常数与系统环境有关,使用上无须输入值。

常数名称

功能说明

传回值范例

os.name

传回‘作业系统环境名称’字串

'nt', 'posix'

sys.platform

传回‘作业平台名称’字串

'win32', 'linux-i386'

os.environ

传回‘系统环境变数’辞典集

{'HOME': '/home/marr', 'SHELL': '/bin/bash'}

os.curdir

代表‘现行工作目录’

'.'

os.pardir

代表‘父层目录’

'..'

既然 os.environ 传回的是辞典集,我们还可以透过 os.environ["PATH"] 方式来取出特定的环境变数值。另外像 sys.argv 是经常被利用到的变数,可以参考下列的例子:

example$ cat prn_argv.py

#!/usr/bin/python

import sys

if len(sys.argv) < 2:

print "enter some arguments with this script."

sys.exit(0)

else:

print "there are %d arguments: " % (len(sys.argv),)

for n in range(len(sys.argv)):

print sys.argv[n],

example$ ./prn_argv.py

enter some arguments with this script.

example$ ./prn_argv.py argu1 argu2 argu3

there are 4 arguments:

./prn_argv.py argu1 argu2 argu3

下列的函式与档案资讯有关,使用上必须输入字串变数 (通常就是档案名称)

函式名称

函式功能

操作范例

os.path.exists()

测试字串变数是否存在。

os.path.exists('/etc/passwd')

os.path.isdir()

测试字串变数是否为目录。

os.path.isdir('/etc')

os.path.isfile()

测试字串变数是否为档案。

os.path.isfile('/etc/X11/X')

os.path.islink()

测试字串变数是否为连结档案。

os.path.islink('/etc/X11/X')

os.path.samefile()

测试两个字串变数是否指向同一档案。

os.path.samefile('/bin/sh', '/bin/bash')

os.path.isabs()

测试字串变数是否为绝对路径。

os.path.isabs('/home/marr')

os.path.getsize()

传回字串变数之档案大小。

os.path.getsize('/etc/passwd')

os.path.getatime()

传回字串变数之最后存取时间。

os.path.getatime('/etc/passwd')

os.path.getmtime()

传回字串变数之最后修改时间。

os.path.getmtime('/etc/passwd')

下列的函式与档案处理有关,使用上必须输入字串变数 (通常包含档案名称)

函式名称

函式功能

操作范例

os.mkdir()

以指定模式建立一个新目录。

os.mkdir("/tmp/beatles", 0700)

os.rmdir()

删除指定目录。

os.rmdir("/tmp/beatles")

os.rename()

将档案或目录名称更改。

os.rename("/tmp/beatles", "/tmp/smiths")

os.link()

为档案建立一个连结。

os.link("/tmp/old_file", "/tmp/ln_file")

os.symlink()

为档案建立一个符号连结。

os.symlink("/tmp/old_file", "/tmp/sln_file")

os.unlink()

删除档案的连结。

os.unlink("/tmp/sln_file")

os.stat()

取得档案完整相关资讯。

os.stat("/etc/X11/X")

os.lstat()

stat() 相同,但不传回连结的档案。

os.lstat("/etc/X11/X")

传统 Unix shell 处理档案名称时,可以应用特殊字元,诸如 *?[A-z] 等,想要取得这样的功能便利,呼叫 glob 模组即可。

>>> import glob

>>> glob.glob("*")

['README', 'all.tex', 'python1.html', 'python2.html', 'python3.txt', 'python4.tex']

>>> glob.glob("python?.html")

['python1.html', 'python2.html']

>>> glob.glob("[A-a]*")

['README', 'all.tex']

>>>

实务程式写作上,可能经常遇到 recursive directory 的问题。函式 os.path.walk() 提供一个简洁的方式来协助处理这类题目,它可以依序读取 directory tree 底下的所有子目录档案,使用时必须接受三个参数,其应用格式如下:

os.path.walk(directory, function, arg)

第一个参数 directory tree 的起始点,第二个参数 function 是打算应用的函式名称,第三个参数 arg 则是可以应用于 function 中的传入值。比较复杂的是第二项参数 function 的实作,它本身也必须包含三个参数,分别是 argsubdirectorynames。这里的参数 arg 即是os.path.walk() 所传来的参数值,参数 subdirectory 是正在处理当中的目录名称,而参数 names 则是 os.listdir(subdirectory) 的档案名称串列结果。下列是一个应用 os.path.walk() 的简单范例。

example$ cat walk_prn.py

#!/usr/bin/python

import os

def printFunction(arg, directory, names): print directory, '\n', names, len(names)

os.path.walk(os.path.getcwd(), printFunction, None)

档案存取功能

Python 以‘档案物件’(file object) 来处理档案的读写功能,语法及概念上,仍与 CPerl 语言里有相似之处,我们可以直接观察下列的范例:

>>> fileobject = open("/etc/group", 'r') Œ

>>> fileobject.tell() 

0

>>> fileobject.readline() Ž

'root:x:0:root\012'

>>> fileobject.tell() 

14

>>> fileobject.readline() Ž

'bin:x:1:root,bin,daemon\012'

>>> fileobject.read(6) 

'daemon'

>>> fileobject.seek(-25, 2)

>>> fileobject.readlines() 

['mysql:x:237:\012', 'marr:x:501:\012']

>>> fileobject.close() Œ

>>>

Œ 档案物件的建立 (开启),是透过函式 open() 来完成,最后可以透过 close() 的物件方法来关闭。而开启的方式,最简单的便是上述的 read ('r') 方式,如果要开启档案供写入资料,则是类似 open("myfile", 'w') 语法,代表要 write ('w') 到档案里头。另外还有 append ('a') 的开启方式,此一模式会将指标位置预设移到档案末尾处,以便使用者新增资料。

 档案物件的资料存取,与‘指标位置’(position) 密切相关,刚开始读进一个新的档案物件,指标位置被设定为 0,表示在最开头处,随着资料的读取,指标位置就会跟着变动,我们可以不时透过物件方法 tell()来得知现行的指标位置。

Ž 物件方法 readline() 可以逐行读进档案物件的内容,较精确点的描述,是由现行指标位置读至换行符号 '\n' 为止,传回之资料型别为字串。

 使用 readlines() 的话,可以由现行指标位置一次读至 EOF,传回之资料型别为字串串列。

 物件方法 read() 可以依使用者的需求,读进特定位元组的资料,如 read(6) 就是由指标位置开始,读进六个字元,传回之资料型别仍为字串。

透过物件方法 seek(),我们可以‘随心所欲’地调整指标位置,使用时,通常必须提供二个输入值,分别是位移值 (offset) 与位移模式 (mode)。它共支援三种位移模式:即‘绝对位置’、‘相对位置’、‘档尾相对位置’,分别以 012 来表示,不过位移模式的预设值是 0。如 seek(14, 0) 是指移至指标位置为 14 的地方,seek(-5, 1) 是指由现行指标位置减少 5,而 seek(-25, 2) 是指由档案末尾处,减少 25 个位移值。

有关字串或串列资料写入档案的动作,可以透过物件方法 write() writelines() 来完成,在此我们并不详加介绍,读者很容易在函式库手册中查到使用方法。

萤幕之输出入功能

raw_input() input() 是两个基本的内建函式,都可以用来协助处理萤幕输出入的动作。

>>> x = raw_input("enter a string from standard input: ") Œ

enter a string from standard input: my string

>>> x

'my string'

>>> x = int(raw_input("enter a number: ")) 

enter a number: 17

>>> x

17

>>> y = input("enter a data object: ") Ž

enter a data object: 18

>>> y = input("enter a data object: ") Ž

enter a data object: 'my data'

>>> y = input("enter a data object: ") Ž

enter a data object: 3 + 6

>>> y

9

>>> input("enter a data object: ") 

enter a data object: my string

Traceback (innermost last):

File "<stdin>", line 1, in ?

File "<string>", line 1

my string

^

SyntaxError: unexpected EOF while parsing

>>>

Œ 函式 raw_input() 可以接受一段字串作为‘提示’,使用者在提示词之后所输入的资料会被当作字串处理。值得注意的是,输入资料列最后的换行符号已‘自动’被处理掉了。

 如果有意将 raw_input() 得到的输入值,其资料型别由字串改为整数,可以再套用函式 int()

Ž 函式 input() 可以视作‘更一般化’的标准输出入工具,在上述的例子中,我们观察到,可以当作输入资料的类型有数值、字串、表示式等。

 当函式 input() 要读进一个字串时,必须明确以 'my string' 之类的表示方法输入,不然会发生 SyntaxError 之类的错误。

进一步来看,实际上,函式 raw_input() input() 都是应用 standard inputstandard output 之输出入功能来实作,这部份的逻辑就和 C 程式语言的传统相仿,如果想要低阶地控制‘标准输入’(standard input)、‘标准输出’(standard output)、‘标准错误’(standard error) 的动作,应该配合载入 sys 模组。

>>> import sys

>>> type(sys.stdout) Œ

<type 'file'>

>>> sys.stdout Œ

<open file '<stdout>', mode 'w' at 80b38c8>

>>> sys.stdin Œ

<open file '<stdin>', mode 'r' at 80b36c0>

>>> sys.stdout.write("Write to the standard output.\n") Œ

Write to the standard output.

>>> s = sys.stdin.readline() Œ

line data from standard input

>>> s

'line data from standard input\012'

>>> f = open("my_file.txt", 'w') 

>>> sys.stdout = f 

>>> sys.stdout.writelines(["first line.\n", "second line.\n"]) 

>>> print "a line from print statement." 

>>> 5 + 12 

>>> sys.stdout = sys.__stdout__ Ž

>>> f.close()

>>> 6 + 12 

18

>>>

example$ cat my_file.txt

first line.

second line.

a line from print statement.

17

Œ 如果熟悉一般档案的存取规则,那么依此类推,sys.stdout sys.stdin 的操作也就显得容易理解,可以将之视为系统预设开启的档案物件,其中 <stdout> 之开启模式为 'w'<stdin> 之开启模式为 'r'

 这里示范的是‘标准输出转向’(redirection) 动作,先开启一个档案物件 f,然后以 sys.stdout = f 叙述式,指定标准输出的内容将转向至档案物件 f。其后的 writelines()print 等相关使用到标准输出的动作,其资料输出都会被写入档案物件 f 里头。

Ž 记得将标准输出的‘原状’恢复,透过 sys.stdout = sys.__stdout__ 叙述即可完成。

 再次测试标准输出的结果,可以发现资料输出又回到萤幕上。另外观察档案 my_file.txt 的内容,可以发现之前因转向而写入的资料。

物件之输出入功能

读者已经了解一般档案的存取方式,也见识了标准输出入的处理技巧,而 Python 另一项实用的技巧,就在于物件资料的快速存取能力,这点一般是透过 cPickle 模组的支援。早期 Python 程式里就存在有一个 pickle 模组,因为执行效率的考量,多数都改用 cPickle 这个由 C 程式语言所改写的模组,除非程计员有意从 pickle 模组继承出新的类别。而 cPickle 应该都可以在新版的 Python 程式套件中找到。

程式执行过程中,或是执行结果所产生的资料,经常会被下次的程式执行所再利用。如果以纯文字档型式将资料予以储存,或许会有便于查看的好处,但是重复应用上的效率总是不够理想,如果能够直接将物件以档案型式储存,然后又快速读出恢复为原有物件,应用上有其优势,而 Python 将这类动作称为‘序列化’(serializing pickling) 及‘反序列化’(unpickling)

#!/usr/bin/python

import cPickle

name_list = ['Morrissey', 'Johnny Marr', 'Andy Rourke', 'Mike Joyce']

output = open("name_file", 'w')

cPickle.dump(name_list, output)

output.close()

input = open("name_file", 'r')

loaded_list = cPickle.load(input)

input.close()

print loaded_list

cPickle 模组的使用,最简易的方式就是利用 dump() 函式来将物件序列化,并利用 load() 函式来反序列化,dump() 函式须指定欲储存之物件及档案物件名称,load() 函式则须给定档案物件名称。下列则是另一个更接近实务的例子:

#!/usr/bin/python

import cPickle

name_list = ['John Lennon', 'Paul McCartney', 'George Harrison', 'Ringo Starr']

birth_yr = [1940, 1942, 1943, 1940]

play_list = ['guitar', 'bass', 'guitar', 'drum']

save_data = (name_list, birth_yr, play_list)

output = open("save_file", 'w')

cPickle.dump(save_data, output)

output.close()

input = open('save_file', 'r')

(name, birth, play) = cPickle.load(input)

input.close()

print name

print birth

print play

延续上述的例子,我们可以使用 shelve 模组,改写出一个足够广泛应用的范例。

#!/usr/bin/python

import shelve

name_list = ['John Lennon', 'Paul McCartney', 'George Harrison', 'Ringo Starr']

birth_yr = [1940, 1942, 1943, 1940]

play_list = ['guitar', 'bass', 'guitar', 'drum']

shv = shelve.open("save_shv")shv['name'] = name_list

shv['birth'] = birth_yr

shv['play'] = play_list

shv.close()

shv = shelve.open("save_shv")

key_list = shv.keys()

for key in key_list:

print shv[key]

shv.close()

上述的范例程式,在技术概念上是相当深入了,不过乍看之下还算易懂,所以一并讨论。如果有人对‘序列化’的资料还是觉得不够方便,譬如说,当储存在档案里的资料量过于庞杂,想要找回所需要的物件可能就增加了难度,此时利用‘索引’来增加可读性,便是值得考虑的做法。shelve 模组提供类似 cPickle 模组的功能,但是其传回值的储存方式类似辞典集,称为书架 (shelf) 物件型别,所以事后可以透过键值来快速取回原有的物件内容。

例外处理 (exception)

和其他物件导向式语言类似,Python 语言也是透过 try ... except 这样的语法结构来处理例外事件。相较来看,Java C++ 的语法结构是以 try ... catch 来处理例外,而使用 throw 来产生例外。例外事件也是物件,在 Python 语言不但随处易见,而且有其物件类别体系,我们早已经在先前的内容中,不只一次看到‘例外’物件,介绍过的都是伴随内建函式操作时的场合。

TABLE_BEGIN

: 常见之内建例外

例外类别

说明

Exception

所有例外

StandardError

标准例外

ArithmeticError

数学例外

FloatingPointError

浮点运算错误

OverflowError

运算溢位错误

ZeroDivisionError

除数为零错误

AttributeError

属性名称不存在

AssertionError

assert 叙述所抛出的例外

EnvironmentError

Python 以外程式所发生的错误

IOError

I/O 错误或档案存取错误

OSError

作业系统错误

EOFError

到达档案结尾所抛出的例外

ImportError

叙述所导致的汇入错误

KeyboardInterrupt

由中断键 (一般是 Ctrl + C) 所抛出的例外

LookupError

索引或键值错误

IndexError

超出序数的位移范围

KeyError

不存在的索引键值

MemoryError

记忆体不足的错误

NameError

区域名称或全域名称搜寻错误

RuntimeError

一般性例外撷取错误

NotImplementedError

尚未实作完成之错误

SyntaxError

语法解析错误

SystemError

Python 直译器的小错误

SystemExit

sys.exit() 所抛出的例外

TypeError

型别传递错误

ValueError

型别无效

TABLE_END

例外本身呈现阶层式架构,只要是分在同一组的,都可以直接用该组最上层的例外名称来代表,例如 LookupError 可用来代表撷取 IndexError KeyError 两种例外,而 StandardError 则可以用来代表几乎所有的例外了。

例外事件之所以‘等同于’错误,主要原因之一,是我们学习 Python 至此,并未主动对于例外采取任何处理措施。除了程式逻辑错误所导致的错误外,许多时候,例外是容易事先被预期的,譬如说,想要开启某个原本以为已存在的档案,或是连线读取某个网路上的资料库,经常会因为时地改变,而导致无法顺利执行完成,这类非关键性的例外,程式人员可以考虑妥善予以处理。

try:

body

except exceptionType1, var1:

exceptionCode1

except exceptionType2, var2:

exceptionCode2

...

...

except:

defaultExceptionCode

else:

elseBody

上述的语法结构,就是 Python 例外处理的流程示意,我们配合下列的例子来实际体会。

example$ cat exception.py

#!/usr/bin/python

class EmptyFileError(Exception):

pass

FileNames = ['my_file', 'non_existent', 'empty_file']

for file in FileNames:

try:

fo = open(file, 'r')

line = fo.readline()

if line == "":

fo.close()

raise EmptyFileError("%s: is empty" % (file))

except IOError, error:

print "%s: could not be opened: %s" % (file, error[1])

except EmptyFileError, error:

print error[0]

else:

fo.seek(0)

lines = fo.readlines()

print "%s: %d lines" % (file, len(lines))

fo.close()

example$ ./exception.py

my_file: 2 lines

non_existent: could not be opened: No such file or directory

empty_file: is empty

Python 透过 raise 语法来产生例外物件,其中的 error 变数储存着例外发生后可以显示的讯息内容。

相关说明

[1] 来点不一样的吧,欢迎访阅 Instant Python 网页,网址 http://www.hetland.org/python/instant-python.php

[2] 想让 Python 执行更有效率吗? 试试Python Performance Tips 网页,网址http://musi-cal.mojam.com/~skip/python/fastpython.html

[3] Python for Lisp Programmers 网页,网址 http://www.norvig.com/python-lisp.html

[4] A Comparison of Python and LISP 网页,网址http://www.strout.net/python/pythonvslisp.html

[5] A Python/Perl phrasebook 网页,网址http://starship.python.net/~da/jak/cookbook.html

[6] Major Python changes 网页,提供Python 改版增修重点说明,网址http://shell.rmi.net/~lutz/errata-python-changes.html

[7] Python Language Mapping 网页,网址http://www.informatik.hu-berlin.de/~loewis/python/pymap.htm