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 + y、x * y、s[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 的实作,它本身也必须包含三个参数,分别是 arg、subdirectory、names。这里的参数 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) 来处理档案的读写功能,语法及概念上,仍与 C、Perl 语言里有相似之处,我们可以直接观察下列的范例:
>>> 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)。它共支援三种位移模式:即‘绝对位置’、‘相对位置’、‘档尾相对位置’,分别以 0、1、2 来表示,不过位移模式的预设值是 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 input、standard 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