在文件處理和網絡編程時,對於打開的文件不管最後內容處理是否符合預期都要在結束時關閉文件。這時常見的處理方法是try catch finally
的方法
f = open("demo.txt", "a")
try:
f = open("demo.txt", "a")
raise Exception("讀取內容報錯")
except Exception as e:
print(e)
finally:
if f is not None:
f.close()
使用錯誤捕獲的方法有效的避免了文件打開沒有關閉的情況。實現同樣的功能有一種更加優雅的方法,那就是with
關鍵字。
with 語法介紹:
上下文管理器(context manager)是 Python2.5 開始支持的一種語法,用於規定某個對象的使用範圍。一旦進入或者離開該使用範圍,會有特殊操作被調用。它的語法形式是 with…as…,主要應用場景資源的創建和釋放。例如,文件就支持上下文管理器,可以確保完成文件讀寫後關閉文件句柄。
簡單示例如下:
with open("demo.txt", "a+") as f:
print("文件讀取")
使用with就可以不用寫冗長的try catch finally等處理流程,而且文件一定是安全打開和關閉。with之所以能做到是因爲open對象中有打開文件的方法__enter__
和關閉文件的方法__exit__
,在執行到print之前with調用了__enter__
方法,執行完print之後with調用了__exit__
方法。並且不管打開文件之後是否會出錯,with最終都會調用__eixt__
方法。所以文件一定能安全關閉。
with 使用
一個對象想要支持with這種優雅的方法來管理,需要實現__enter__
和__exit__
兩個方法。根據with是否有返回,可以分成兩個類型。
- 沒有返回值,如
with open("demo.txt")
; - 有返回值,如
with open("demo.txt") as f
;
沒有返回值
class ContextDemo:
def __init__(self):
print("__init__")
return
def __enter__(self):
print("__enter__")
def __exit__(self, exc_type, exc_val, exc_tb):
print("__exit__")
if __name__ == '__main__':
with ContextDemo():
print("我就是with中的操作")
__init__
__enter__
我就是with中的操作
__exit__
有返回值
with 的返回值實際上就是__enter__
中return的結果。有return就有返回值,沒有return就沒有返回值
class ContextDemo:
def __init__(self):
print("__init__")
return
def __enter__(self):
print("__enter__")
return 100
def __exit__(self, exc_type, exc_val, exc_tb):
print("__exit__")
if __name__ == '__main__':
with ContextDemo() as num :
print(f"我就是with中的操作,輸出{num}")
__init__
__enter__
我就是with中的操作,輸出100
__exit__
contextlib
contextlib是一個上下文管理器工具,工具中有一個contextmanager,可以將一個函數變成一個裝飾器,用來支持with關鍵字。
通俗來說就是如果一個函數想要支持with就需要有__enter__
和__exit__
這兩個方法。而contextmanager
就可以將函數變成這樣一個函數。
依然根據with是否有返回值,區分兩中使用方法
不帶返回值
from contextlib import contextmanager
@contextmanager
def manager_fun():
print("enter")
yield
print("exit")
with manager_fun():
print("我就是with中的操作")
enter
我就是with中的操作
exit
函數中通過yield將函數分成三個部分:yield之前代碼、yield之後代碼、yield;
執行的流程也是三個步驟:
with manager_fun
with 進入時執行yield之前的代碼部分print("enter")
- 遇到yield中斷,然後執行
print("我就是with中的操作")
- 最後返回到yield後面,執行
print("exit")
傳統上下文管理器需要實現的__enter__
和__exit__
在contextlib中對應的就是:yield之前代碼就是__enter__
,yield代碼之後就是__exit__
帶返回值
from contextlib import contextmanager
@contextmanager
def manager_fun():
print("enter")
yield 100
print("exit")
with manager_fun() as num:
print(f"我就是with中的操作{num}")
enter
我就是with中的操作100
exit
with 可以有返回值,如果想實現返回值,在傳統上下文管理器中是__enter__
函數的返回值就是with的返回值,在contextlib中就是yield的返回值。
yield 返回一個值,然後程序中斷暫停在這裏。返回的值就是with的返回值。(聽起來有點繞口)
contextlib 使用場景
有些場景下想要使用with來管理對象,但是目標對象並不支持,這時就可以通過contextlib來包裝一個上下文管理器,達到管理資源的目的