python语句解释_深入理解python with 语句
深入理解python with 語句
python中with 語句作為try/finally 編碼范式的一種替代, 適用于對資源進行訪問的場合,確保不管使用過程中是否發(fā)生異常都會執(zhí)行必要的”清理”操作,釋放資源,比如文件使用后自動關(guān)閉、線程中鎖的自動獲取和釋放等
1. 使用with打開文件
你應(yīng)該見過下面這種打開文件的方式
with open('data', 'r', encoding='utf-8') as f:
data = f.readlines()
上面的寫法,與下面的寫法在最終效果上是一致的
f = open('data', 'r', encoding='utf-8')
try:
data = f.readlines()
except:
pass
finally:
f.close()
對比兩段代碼不難發(fā)現(xiàn),使用with語句時,代碼更加簡潔,而且不用主動關(guān)閉文件,在with語句體退出時,會自動關(guān)閉文件,即便with語句體中發(fā)生了異常。
2. 上下文管理器和with 語句有關(guān)的概念
想要理解with語句,就必須先理解以下幾個概念
2.1 上下文管理協(xié)議
簡單來說,就是實現(xiàn)兩個方法,__enter__() 和__exit__()
2.2 上下文管理器
實現(xiàn)了__enter__() 和__exit__()的對象就是上下文管理器
2.3 運行時上下文
由上下文管理器創(chuàng)建,在with語句體代碼執(zhí)行前,通過__enter__()進入,語句體代碼執(zhí)行結(jié)束后,通過__exit__()退出
2.4 上下文表達式
在with關(guān)鍵字后面的表達式,表達式返回上下文管理器對象
2.5 語句體
with語句包裹起來的代碼
3. 使用with語句控制線程鎖的釋放
使用with不僅能夠自動的關(guān)閉打開的文件對象,還可以自動的釋放線程鎖,這樣可以避免死鎖的發(fā)生,在python多線程---線程鎖一文中,為避免多個線程同時對一個變量對象進行修改,在關(guān)鍵語句上加了線程鎖
def worker():
time.sleep(1)
global a
for i in range(100000):
m_lock.acquire() # 加鎖
a += 1
m_lock.release() # 釋放鎖
如果你忘記了寫m_lock.release() 對鎖進行釋放,那么這將導(dǎo)致其他線程永遠(yuǎn)也無法獲取到線程鎖,這樣就形成了死鎖,上面的代碼在acquire之后,使用release釋放所,使用with語句,可以更加優(yōu)雅的實現(xiàn)加鎖和釋放鎖的操作。
def worker():
time.sleep(1)
global a
for i in range(100000):
with m_lock:
a += 1
4. 同時打開多個文件
許多人都不知道,with語句可以同時打開多個文件,這樣做可以減少代碼的縮進,讓代碼的編寫更加容易,兩個open語句之間用逗號分隔即可。
with open('a1', 'w')as f1, open('a2', 'w')as f2:
f1.write('a')
f2.write('b')
5. 自定義上下文管理器
在調(diào)試程序性能時,如果只是想知道某個函數(shù)的執(zhí)行時長,可以使用一個可以統(tǒng)計函數(shù)運行時長的裝飾器進行處理,但程序往往很復(fù)雜,一段代碼里,要做很多操作,不只是調(diào)用了一個函數(shù),也可能存在循環(huán),因此,單純的知道某個函數(shù)的執(zhí)行時長,不能幫助我們更好的了解程序的性能。
我們需要針對某個代碼段進行時間統(tǒng)計,知道這一段代碼的執(zhí)行時長對我們很有幫助。你可以使用time.time()方法在代碼段開始時獲取到時間,在結(jié)束時再次獲取到時間,兩個時間做差就可以得到這個代碼段的運行時長,這種操作方式寫起來很麻煩,如果有多處代碼段需要統(tǒng)計,就得寫多次,很不方便。
下面是一個可以統(tǒng)計代碼段運行時長的上下文管理器
import time
class ProTime(object):
def __init__(self, tag=''):
self.tag = tag
def __enter__(self):
self.start_time = time.time()
def __exit__(self, exc_type, exc_val, exc_tb):
self.end_time = time.time()
time_diff = self.end_time - self.start_time
msg = "代碼段{tag}運行時長{time_diff}".format(tag=self.tag, time_diff=time_diff)
print(msg)
with ProTime('first') as pt:
# 這里是你要統(tǒng)計運行時長的代碼塊
time.sleep(1)
with ProTime('second') as pt:
# 這里是你要統(tǒng)計運行時長的代碼塊
time.sleep(2)
理解這段代碼的關(guān)鍵之處,在with語句所包裹的語句體執(zhí)行之前,先要執(zhí)行__enter__方法,語句體執(zhí)行結(jié)束之后,不論是否有異常,都要執(zhí)行__exit__,在__exit__方法里,三個參數(shù)提供了異常的全部信息,如果你想處理異常,可以在這個方法里做處理。
__init__ 方法有一個tag參數(shù),設(shè)置這個參數(shù)的目的,是為了在輸出信息里區(qū)分多個代碼塊,如果不想設(shè)置這個tag,可以考慮對這個上下文管理器進行修改,通過調(diào)用棧獲得調(diào)用信息,準(zhǔn)確的指出是哪個代碼段的執(zhí)行時長。
修改后的上下文管理器如下
import time
import sys
class ProTime(object):
def __init__(self, tag=''):
frame = sys._getframe()
tag_frame = frame.f_back
self.lineno = tag_frame.f_lineno
self.filename = tag_frame.f_code.co_filename
self.tag = tag
def __enter__(self):
self.start_time = time.time()
def __exit__(self, exc_type, exc_val, exc_tb):
self.end_time = time.time()
time_diff = self.end_time - self.start_time
if self.tag:
msg = "代碼段{tag}運行時長{time_diff}".format(tag=self.tag, time_diff=time_diff)
else:
msg = "文件{filename} 第 {lineno} 行代碼塊執(zhí)行時長{time_diff}".format(filename=self.filename, lineno=self.lineno, time_diff=time_diff)
print(msg)
with ProTime('first') as pt:
# 這里是你要統(tǒng)計運行時長的代碼塊
time.sleep(1)
with ProTime() as pt:
# 這里是你要統(tǒng)計運行時長的代碼塊
time.sleep(2)
def test():
with ProTime() as pt:
# 這里是你要統(tǒng)計運行時長的代碼塊
time.sleep(1)
test()
總結(jié)
以上是生活随笔為你收集整理的python语句解释_深入理解python with 语句的全部內(nèi)容,希望文章能夠幫你解決所遇到的問題。
- 上一篇: python无法打开_如何解决Windo
- 下一篇: python参数类型_Python 参数