天天看點

python的with 淺談 Python 的 with 語句

with 語句适用于對資源進行通路的場合,確定不管使用過程中是否發生異常都會執行必要的“清理”操作,釋放資源。

比如檔案使用後自動關閉、線程中鎖的自動擷取和釋放等。

with open('test.txt', 'r') as f:
    for line in f:
        print(line)      

運作機制

with VAR = EXPR:
    BLOCK      

等價于

VAR = EXPR
VAR.__enter__()
try:
    BLOCK
finally:
    VAR.__exit__()      

VAR對應一個上下文管理器(context manager)。

上下文管理器

實作了__enter__()和__exit__()兩個方法的類就是一個上下文管理器。

class MyContextManager:
    def __enter__(self):
        print('before block run')
    def __exit__(self, exc_type, exc_val, exc_tb):
        print('after block run')      

使用上下文管理MyContextManager

with MyContextManager():
    print('run block')      

輸出如下:

before block run

run block

after block run

MyContextManager也可以接受參數

class MyContextManager:
    def __init__(self, name, age):
        self.name = name
        self.age = age

    def __enter__(self):
        print('before block run')
        return self

    def __exit__(self, exc_type, exc_val, exc_tb):
        print('after block run')

    def show(self):
        print('my name is:', self.name)
        print('my age is:', self.age)      

再次使用上下文管理MyContextManager

with MyContextManager('logan', 27) as myCM:
    myCM.show()      

my name is: logan

my age is: 27

這裡用到了一個as: with context_manager as target

target對應context_manager的__enter__()方法的傳回值,傳回值可以是context_manager自身,也可以是其他對象。

contextlib.contextmanager

 contextmanager可以對生成器函數進行裝飾,傳回結果是一個上下文管理器。

import contextlib

@contextlib.contextmanager
def MyGenerator():
    print('before yield')
    yield
    print('after yield')

with MyGenerator():
    print('run block')      

before yield

after yield

這裡yield nothing,下面給個yield something的例子

import contextlib

@contextlib.contextmanager
def MyGenerator():
    print('before yield')
    yield 'context manager'
    print('after yield')

with MyGenerator() as cm:
    print('run block')
    print(cm)      

context manager

兩個注意點

1. yield之前的語句類似于__enter__(),yield之後的語句類似于__exit__()方法。

2. yield傳回一個上下文管理器,如果使用as語句,會被指派給as語句中的target。

try...finally

當在try範圍内産生一個異常時,會立即跳轉到finally語句塊。當finally語句塊執行完畢後,會繼續向上一層引發異常。

參考資料:

淺談 Python 的 with 語句