python语句解释_深入理解python with 语句
深入理解python with 語句
python中with 語句作為try/finally 編碼范式的一種替代, 適用于對資源進行訪問的場合,確保不管使用過程中是否發生異常都會執行必要的”清理”操作,釋放資源,比如文件使用后自動關閉、線程中鎖的自動獲取和釋放等
1. 使用with打開文件
你應該見過下面這種打開文件的方式
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()
對比兩段代碼不難發現,使用with語句時,代碼更加簡潔,而且不用主動關閉文件,在with語句體退出時,會自動關閉文件,即便with語句體中發生了異常。
2. 上下文管理器和with 語句有關的概念
想要理解with語句,就必須先理解以下幾個概念
2.1 上下文管理協議
簡單來說,就是實現兩個方法,__enter__() 和__exit__()
2.2 上下文管理器
實現了__enter__() 和__exit__()的對象就是上下文管理器
2.3 運行時上下文
由上下文管理器創建,在with語句體代碼執行前,通過__enter__()進入,語句體代碼執行結束后,通過__exit__()退出
2.4 上下文表達式
在with關鍵字后面的表達式,表達式返回上下文管理器對象
2.5 語句體
with語句包裹起來的代碼
3. 使用with語句控制線程鎖的釋放
使用with不僅能夠自動的關閉打開的文件對象,還可以自動的釋放線程鎖,這樣可以避免死鎖的發生,在python多線程---線程鎖一文中,為避免多個線程同時對一個變量對象進行修改,在關鍵語句上加了線程鎖
def worker():
time.sleep(1)
global a
for i in range(100000):
m_lock.acquire() # 加鎖
a += 1
m_lock.release() # 釋放鎖
如果你忘記了寫m_lock.release() 對鎖進行釋放,那么這將導致其他線程永遠也無法獲取到線程鎖,這樣就形成了死鎖,上面的代碼在acquire之后,使用release釋放所,使用with語句,可以更加優雅的實現加鎖和釋放鎖的操作。
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. 自定義上下文管理器
在調試程序性能時,如果只是想知道某個函數的執行時長,可以使用一個可以統計函數運行時長的裝飾器進行處理,但程序往往很復雜,一段代碼里,要做很多操作,不只是調用了一個函數,也可能存在循環,因此,單純的知道某個函數的執行時長,不能幫助我們更好的了解程序的性能。
我們需要針對某個代碼段進行時間統計,知道這一段代碼的執行時長對我們很有幫助。你可以使用time.time()方法在代碼段開始時獲取到時間,在結束時再次獲取到時間,兩個時間做差就可以得到這個代碼段的運行時長,這種操作方式寫起來很麻煩,如果有多處代碼段需要統計,就得寫多次,很不方便。
下面是一個可以統計代碼段運行時長的上下文管理器
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:
# 這里是你要統計運行時長的代碼塊
time.sleep(1)
with ProTime('second') as pt:
# 這里是你要統計運行時長的代碼塊
time.sleep(2)
理解這段代碼的關鍵之處,在with語句所包裹的語句體執行之前,先要執行__enter__方法,語句體執行結束之后,不論是否有異常,都要執行__exit__,在__exit__方法里,三個參數提供了異常的全部信息,如果你想處理異常,可以在這個方法里做處理。
__init__ 方法有一個tag參數,設置這個參數的目的,是為了在輸出信息里區分多個代碼塊,如果不想設置這個tag,可以考慮對這個上下文管理器進行修改,通過調用棧獲得調用信息,準確的指出是哪個代碼段的執行時長。
修改后的上下文管理器如下
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} 行代碼塊執行時長{time_diff}".format(filename=self.filename, lineno=self.lineno, time_diff=time_diff)
print(msg)
with ProTime('first') as pt:
# 這里是你要統計運行時長的代碼塊
time.sleep(1)
with ProTime() as pt:
# 這里是你要統計運行時長的代碼塊
time.sleep(2)
def test():
with ProTime() as pt:
# 這里是你要統計運行時長的代碼塊
time.sleep(1)
test()
總結
以上是生活随笔為你收集整理的python语句解释_深入理解python with 语句的全部內容,希望文章能夠幫你解決所遇到的問題。
- 上一篇: python无法打开_如何解决Windo
- 下一篇: 苹果 CEO 库克 2022 年总薪酬达