在Python中使用异常和文件时的清理
我学习Python已经几天了,但对它的“精神”有点摸不着头脑。之前我主要用C/C++/Java/Perl,所以我知道Python和C完全不一样,这也是我想理解Python的“精神”,以便能更好地使用它(到目前为止,这确实有点难)...
我特别想了解的是异常处理和清理的部分:这篇文章最后的代码是为了模拟一个比较常见的情况,就是打开和解析文件时,如果出错需要关闭文件...
我看到的大多数示例都使用了try语句的'else'部分来关闭文件... 这让我觉得很合理,直到我意识到错误可能是因为:
- 打开文件时出错(这种情况下就不需要关闭没有打开的文件)
- 解析文件时出错(这种情况下需要关闭文件)
这里的陷阱是,如果你在try块中使用'else'部分,那么如果在解析时出错,文件就不会被关闭!另一方面,使用'finally'部分又会导致需要额外的检查,因为如果在打开文件时出错,file_desc变量可能根本不存在(见下面代码中的注释)...
这种额外的检查效率低下,而且很麻烦,因为任何合理的程序可能会包含成百上千个符号,而解析dir()的结果真是让人头疼... 更不用说这样的语句可读性差了...
其他大多数语言允许变量定义,这样可以解决这个问题... 但在Python中,一切似乎都是隐式的...
通常情况下,你只需声明一个file_desc变量,然后为每个任务使用多个try/catch块... 一个用于打开,一个用于解析,最后一个用于关闭... 不需要嵌套它们... 但在这里我不知道怎么声明这个变量... 所以我在问题的开始就卡住了!
那么,Python在这里的“精神”是什么呢???
- 把打开和解析分成两个不同的方法?怎么做?
- 使用某种嵌套的try/except块???怎么做?
- 也许有办法声明file_desc变量,这样就不需要额外的检查了... 这可能吗???值得吗???
- 那close()语句呢???如果它抛出错误怎么办?
谢谢你的提示... 下面是示例代码:
class FormatError(Exception):
def __init__(self, message):
self.strerror = message
def __str__(self):
return repr(message)
file_name = raw_input("Input a filename please: ")
try:
file_desc = open(file_name, 'r')
# read the file...
while True:
current_line = file_desc.readline()
if not current_line: break
print current_line.rstrip("\n")
# lets simulate some parsing error...
raise FormatError("oops... the file format is wrong...")
except FormatError as format_error:
print "The file {0} is invalid: {1}".format(file_name, format_error.strerror)
except IOError as io_error:
print "The file {0} could not be read: {1}".format(file_name, io_error.strerror)
else:
file_desc.close()
# finally:
# if 'file_desc' in dir() and not file_desc.closed:
# file_desc.close()
if 'file_desc' in dir():
print "The file exists and closed={0}".format(file_desc.closed)
else:
print "The file has never been defined..."
5 个回答
从Python 2.5开始,新增了一个叫做with
的命令,它可以让你在编程时更简单地处理一些事情。想了解更多,可以点击这里。下面是你代码的一个改进版本:
class FormatError(Exception):
def __init__(self, message):
self.strerror = message
def __str__(self):
return repr(message)
file_name = raw_input("Input a filename please: ")
with open(file_name, 'r') as file_desc:
try:
# read the file...
while True:
current_line = file_desc.readline()
if not current_line: break
print current_line.rstrip("\n")
# lets simulate some parsing error...
raise FormatError("oops... the file format is wrong...")
except FormatError as format_error:
print "The file {0} is invalid: {1}".format(file_name, format_error.strerror)
except IOError as io_error:
print "The file {0} could not be read: {1}".format(file_name, io_error.strerror)
if 'file_desc' in dir():
print "The file exists and closed={0}".format(file_desc.closed)
else:
print "The file has never been defined..."
只是提醒一下:你可以随时声明一个变量,然后它就会变成这样:
file_desc = None
try:
file_desc = open(file_name, 'r')
except IOError, err:
pass
finally:
if file_desc:
close(file_desc)
当然,如果你使用的是更新版本的Python,使用上下文管理器的方式会更好;不过,我想强调的是,如何在Python中通用地处理异常和变量的作用域。
处理这个问题最简单的方法是利用Python 2.5及以上版本中的文件对象是上下文管理器这一特性。你可以使用with
语句来进入一个上下文;当你离开这个with
的范围时,上下文管理器的__exit__
方法会自动被调用。这样,文件对象的上下文管理会自动关闭文件。
try:
with file("hello.txt") as input_file:
for line in input_file:
if "hello" not in line:
raise ValueError("Every line must contain 'hello'!")
except IOError:
print "Damnit, couldn't open the file."
except:
raise
else:
print "Everything went fine!"
比如,打开的hello.txt文件句柄会自动关闭,而在with
范围内产生的异常会被传递到外面。