1. 程式人生 > >with...as的原理

with...as的原理

轉自:https://blog.csdn.net/waspvae/article/details/80490741

以前看過,筆試考到沒回答好。。做個記錄

對於檔案、資料庫連線、socket 等系統資源而言,應用程式開啟這些資源並執行完業務邏輯之後,必須做的一件事就是要關閉(斷開)該資源。否則會一直佔用資源,影響效能。


以向檔案寫入資料為例

普通版:

f = open('file.txt', 'w')
f.write('waspvae')
f.close()

這種寫法會有一個潛在的問題,如果在呼叫 write 的過程中出現了異常會導致後續程式碼無法執行,close 方法無法被呼叫,檔案無法關閉,資源就會一直被該程式佔用,無法釋放。

改良版:

f = open('file.txt', 'w')
try:
    f.write('waspvae')
except IOError:
    print('error')
finally:
    f.close()

改良版雖然解決了出現異常無法關閉的問題,但程式碼有點複雜,不符合 python 精神。

終極版:

with open('file.txt', 'w') as f:
    f.write('waspvae')

終極版更加優雅、簡潔。open 方法的返回值賦值給變數 f,with … as 後面的語句屬於 with 的作用域,當離開 with 作用域的時候,系統能夠自動呼叫 f.close() 方法,那麼它的實現原理是什麼?在講 with 的原理前要涉及到另外一個概念,就是上下文管理器(Context Manager)。

上下文管理器

任何實現了 enter() 和 exit() 方法的物件都可稱之為上下文管理器,上下文管理器物件可以使用 with 關鍵字。檔案物件就是實現了上下文管理器

模擬實現一個自己的檔案類,讓該方法實現 enter() 和 exit() 方法。

class File():

    def __init__(self, filename, mode):
        self.filename = filename
        self.mode = mode

    def __enter__(self):
        print("entering")
        self.f = open(self.filename, self.mode)
        return self.f

    def __exit__(self, *args):
        print("will exit")
        self.f.close()
# 因為 FIle 類實現了上下文管理器,所以可以使用 with 語句
with File('file.txt', 'w') as f:
    print("writing")
    f.write('waspvae')

列印結果為:

entering
writing
will exit

首先呼叫該例項的 enter 方法,列印 entering,把返回結果繫結到變數 f 上,然後列印 writing,當 with 的作用域執行完畢,呼叫 exit 方法,列印 will exit ,關閉檔案。

實現上下管理器的另一方式(contextmanager 裝飾器)

在 contextlib 模組中,提供了 @contextmanager 裝飾器,將一個生成器函式當成上下文管理器使用,上面的程式碼與下面的程式碼等效

from contextlib import contextmanager
@contextmanager
def file(filename, mode):
    print("entering")
    f = open(filename, mode)
    yield f
    print("will exit")
    f.close()
with File('file.txt', 'w') as f:
    print("writing")
    f.write('waspvae')

通過 yield 將函式分割成兩部分,yield 之前的語句在 enter 方法中執行,yield 之後的語句在 exit 方法中執行。緊跟在 yield 後面的值是函式的返回值。

--------------------- 本文來自 Waspvae 的CSDN 部落格 ,全文地址請點選:https://blog.csdn.net/waspvae/article/details/80490741?utm_source=copy