读写文件时使用with
可以很好地避免文件读写出错或者是忘记写关闭文件的情况
例如,打开一个文件读写:
f=open('F:\python\mytest\mytest\.idea\contextlib_code.py')
f.read()
f.close()
或者是:
try:
f=open('F:\python\mytest\mytest\.idea\contextlib_code.py')
f.read()
finally:
f.close()
比较麻烦,使用with
语句可以省事
语法:with open('/path/to/contextlib_code.py', 'r') as f:
with
语句必跟__enter__()
和__exit__()
意义在于:
第一步:执行__enter__()
将其返回值带给with
第二步:执行with
第三步:执行__exit__()
举例:
#-*- coding: utf-8 -*-
"""
#使用with语句,看执行先后顺序
"""
class Query(object):
def __init__(self,name):
self.name=name
def __enter__(self): #第一步执行__enter__
print('Begin')
return self
def __exit__(self,exc_type,exc_value,tracback): #第三步执行:__exit__
if exc_type:
print('Error')
else:
print('End')
def query(self):
print('Query info about %s...' % self.name)
with Query('Bob') as q: #第二部执行:__with__
q.query()
执行结果:
Begin
Query info about Bob...
End
原理是:
with context_expression [as target(s)]:
with-body,
它的执行顺序是这样的
1.执行 context_expression,生成上下文管理器 context_manager
2.调用上下文管理器的 enter() 方法;如果使用了 as 子句,则将 enter() 方法的返回值赋值给 as 子句中的 target(s)
3.执行语句体 with-body
4.不管是否执行过程中是否发生了异常,执行上下文管理器的 exit() 方法,exit() 方法负责执行“清理”工作,如释放资源等。如果执行过程中没有出现异常,或者语句体中执行了语句 break/continue/return,则以 None 作为参数调用 exit(None, None, None) ;如果执行过程中出现异常,则使用 sys.exc_info 得到的异常信息为参数调用 exit(exc_type, exc_value, exc_traceback)
5.出现异常时,如果 exit(type, value, traceback) 返回 False,则会重新抛出异常,让with 之外的语句逻辑来处理异常,这也是通用做法;如果返回 True,则忽略异常,不再对异常进行处理
还有一种方法是使用contextlib
的contextmanager
简化使用__enter__
和__exit__
使用方法:
1.用yield
代替__enter__
,首先执行yield
前的语句,然后执行with
后再次执行yield
后的语句
from contextlib import contextmanager
class Query(object):
def __init__(self,name):
self.name=name
def query(self):
print('Query info about %s...' % self.name)
@contextmanager
def create_query(name):
print('Begin')
q=Query(name)
yield q
print('End')
with create_query('Bob') as q:
q.query()
结果:
Begin
Query info about Bob...
End
当然这两种方法都是需要有上下文的,如果一个对象没有上下文,则没有办法使用with
和contextmanage
@closing
解决了没有上下文的情况
可以用closing()
来把该对象变为上下文对象。例如,用with语句使用urlopen()
:
from contextlib import closing
from urllib.request import urlopen
with closing(urlopen('https://www.python.org'))as page:
for line in page:
print(line)