1. 程式人生 > 其它 >Python上下文管理器的高階使用

Python上下文管理器的高階使用

在檔案處理和網路程式設計時,對於開啟的檔案不管最後內容處理是否符合預期都要在結束時關閉檔案。這時常見的處理方法是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是否有返回,可以分成兩個型別。

  1. 沒有返回值,如with open("demo.txt");
  2. 有返回值,如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;
執行的流程也是三個步驟:

  1. with manager_fun with 進入時執行yield之前的程式碼部分 print("enter")
  2. 遇到yield中斷,然後執行print("我就是with中的操作")
  3. 最後返回到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來包裝一個上下文管理器,達到管理資源的目的