Python 中 open() 方法既能直接返回也能通過with語句當作上下文管理器使用是怎麼做到的?

如題。簡單看了下io.py部分的源碼,只看到了open的定義是直接返回對象,沒有看到是如何實現上下文管理器的。Google了半天也沒有結果。求知乎大神解答!


前段時間果殼 Python 開發面試被問到了這個問題

實現某個對象可以用 with 來管理,只需要改寫 __enter__ 和 __exit__ 這兩個 magic method 即可

另外你說你在 io.py 源碼里沒找到,大哥讀代碼要仔細啊

io.py 里的 IO 函數都是從 _pyio.py 里 import 進來的,然後在 _pyio.py 的第 140 行 到 147 行有這麼一段注釋

open() returns a file object whose type depends on the mode, and

through which the standard file operations such as reading and writing

are performed. When open() is used to open a file in a text mode ("w",

"r", "wt", "rt", etc.), it returns a TextIOWrapper. When used to open

a file in a binary mode, the returned class varies: in read binary

mode, it returns a BufferedReader; in write binary and append binary

modes, it returns a BufferedWriter, and in read/write mode, it returns

a BufferedRandom.

所以 open() 這個函數在不同模式下會返回不同的對象,包括 TextIOWrapper/BufferedReader/BufferedWriter 這三種

繼續看上面這三個類的定義,發現這幾個類還繼承了其他的幾個類,這裡我就不領著你看了,直接用 Visio 畫了個類層次示意圖,如下

其中藍色矩形表示 class,藍色連線表示繼承關係,藍色連線上的數字表示這個繼承關係的代碼在源碼中的哪一行

可以看到 open() 函數所返回的幾個 class,最終都繼承於 IOBase 這個 class,而在這個 class 中,就實現了 __enter__() 和 __exit() 兩個 magic method,具體代碼位於 _pyio.py 的第 428 行到 437 行

### Context manager ###

def __enter__(self):
"""Context management protocol. Returns self."""
self._checkClosed()
return self

def __exit__(self, *args):
"""Context management protocol. Calls close()"""
self.close()

以上,懂了否?


只要有__enter__和__exit__方法,就能用在with語句里,open返回的對象有這兩個方法,就可以了,不必在open方法內做什麼手腳。


你google context manager就有結果了


Python 的上線文管理器使用with關鍵字,用來定義with後面這個縮進級別,進入和跳出的行為,是語法糖的一種。open是一個調用用於實例化file類的函數,而file類只不過是一個擁有__enter__和__exit__(這種前後都有兩個下劃線的在Python中叫做「魔法方法」,除了這兩個還有很多,有興趣可以去查一下)的類,用在with中時,程序會自動在進出這個程序塊時調用這兩個函數,如果沒用with就要手動管理,自己調用close()。如果想要自己實現類似的功能,用回調就,或者單獨封裝成裝飾器(裝飾器其實也是Python的一種語法糖)都可以。


跟Java的Closeable一樣 定義好釋放資源的方式 通過語法糖隱藏部分重複代碼


推薦閱讀:

C++派生類怎樣進行文件讀寫?
如何理解面向組合子編程?
C語言的設計模式有哪些?
面向對象、面向服務、面向組件三種編程模式有什麼區別?分別適用於哪些領域的開發?

TAG:編程語言 | Python | 面向對象編程 |