讀寫文件時使用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)