本文中所有代码均运行在Python 2.7上
相信大多数的新手都曾经犯过这样的错误:在往文件里进行读/写操作的时候,在操作结束之后忘记关闭文件,而导致没有书写结果,就类似下面的代码:
>>> f = open('demo.txt', 'w')
>>> f.write('test')
而正确的写法应该是下面这样:
>>> f = open('demo.txt', 'w')
>>> f.write('test')
>>> f.close()
在对文件操作完之后,应该及时关闭文件。这样做一是为了文件的数据完全,二是节省系统资源,三是防止可能引起的死锁。
第二种写法似乎可以满足我们的要求:在完成文件书写之后,立即关闭了文件。
但问题仍然存在:由于某些程序异常(比如说在写操作的时候磁盘已满)而没有执行关闭文件的操作。那么,有没有方法可以保证文件一定被关闭呢?有些人可能会提出使用try...finally
来处理异常出现时的文件关闭。这种方法是可行的,但不是优雅的。真正被官方推荐的方法是使用with
语句进行操作:
>>> with open('demo.txt', 'w') as f:
>>> f.write('text')
这样是不是简洁多了?
接下来就来看看with...as...
语句的执行过程。
- 计算表达式的值,返回一个上下文管理对象,并把它赋给句柄;
- 分别加载上下文管理器的
__enter__
和__exit__
方法以备调用; - 调用上下文管理器的
__enter__
方法; - 执行
with
下的语句块; - 如果上个步骤正常执行完,则执行
__exit__
; - 如果步骤4执行出现异常,则将异常信息传递给
__exit__
,如果__exit__
执行结果是false
则异常继续向上抛出,否则继续执行程序;
以上的执行机制保证在正常和异常发生的情况下,文件都能被正确的关闭。我们也了解了上下文管理器的概念:通过__enter__
和__exit__
来定义程序运行的环境,处理程序的进入和退出问题。
-
__enter__()
:进入运行时的上下文环境,with中将它的返回值(也就是一个上下文对象)绑定到句柄上(上文中是f
); -
__exit__(exc_type, exc_value, traceback)
:退出运行时的处理方法,可以用于异常处理,现场清理工作;
接下来,我们来自己写一个上下文管理器来作为本文的结尾:
class dbHelper(object):
import config
import time
def __enter__(self):
db.connect(config.db_addr)
print 'db connect at '+str(time.time())
def __exit__(self, exc_type, exc_value, traceback):
db.shutdown()
if not exc_type:
return False
else:
logger.error(exc_type, exc_value, traceback)
return True