首頁 > 軟體

深人瞭解Python上下文管理器

2021-12-08 16:00:19

下面先來介紹一下with關鍵字在檔案讀寫中的應用,簡單瞭解上下文管理器的功能。

with語句

Python檔案及目錄處理方法中介紹了讀寫大檔案建議使用with語句,with語句會進行資源的自動管理。檔案很多的情況下也會導致資源洩露,下面來開啟100000個檔案,不進行檔案關閉操作:

for x in range(100000):
    file = open('test.txt', 'w')
    file_descriptors.append(file)

執行會報如下錯誤:

OSError: [Errno 24] Too many open files: 'test.txt'

原因就是開啟了太多檔案而沒有及時關閉導致了資源洩露,造成系統崩潰。完成處理後需要對檔案進行關閉操作:

file_descriptors = []
for x in range(10000):
	file = open('test.txt', 'w')
	try:
		file_descriptors.append(file)
	finally:
		file.close()

使用 with 語句可以完成自動分配並且釋放資源,比上面的寫法更加簡潔:

file_descriptors = []
for x in range(10000):
	with open('test.txt', 'w') as file:
		file_descriptors.append(file)

上下文管理器建立

基於類的上下文管理器

可以使用類來建立上下文管理器,需要保證這個類包括兩個方法:__enter__() __exit__()。其中,方法 __enter__() 返回需要被管理的資源,方法 __exit__() 進行資源釋放、清理操作。

下面來模擬 Python 的開啟、關閉檔案操作:

class FileManager:
    def __init__(self, name, mode):
        print('__init__ method called')
        self.name = name
        self.mode = mode
        self.file = None
    def __enter__(self):
        print('__enter__ method called')
        self.file = open(self.name, self.mode)
        return self.file
    def __exit__(self, exc_type, exc_value, exc_traceback):
        print('__exit__ method called')
        if self.file:
            self.file.close()
        if exc_type:
            print(f'exc_type: {exc_type}')
            print(f'exc_value: {exc_value}')
            print(f'exc_traceback: {exc_traceback}')
        return True
with FileManager('test.txt', 'w') as f:
	print('開始寫操作')
	f.write('hello world !')
print(f.closed)

執行結果:

__init__ method called
__enter__ method called
開始寫操作
__exit__ method called
exc_type: <class 'Exception'>
exc_value: exception raised
exc_traceback: <traceback object at 0x000001B43C2444C8>
True

可以看到執行順序為:

  • __init__():初始化物件 FileManager
  • __enter__():開啟檔案,返回 FileManager 物件

with中的程式碼

__exit__():關閉開啟的檔案流

__exit__()方法中的引數exc_type, exc_value, 和 exc_traceback 用於管理異常。

@contextmanager 裝飾器

可以使用 contextlib.contextmanager 裝飾器而不使用類的方式來實現上下文管理器,它是基於生成器的上下文管理器,用以支援 with 語句。

仍以開啟、關閉檔案為例:

from contextlib import contextmanager
@contextmanager
def file_manager(name, mode):
    try:
        f = open(name, mode)
        yield f
    finally:
        f.close()
with file_manager('test.txt', 'w') as f:
    f.write('hello world !')

其中 file_manager() 函數是一個生成器,yield 之前可以看成是__enter__方法中的內容,yield 後面的是 __exit__() 內容。加上@contextmanager裝飾器,使用基於生成器的上下文管理器時,不需要定義__enter__()__exit__()方法。

總結

上下文管理器可確保用過的資源得到迅速釋放,通常和 with 語句一起使用,大大提高了程式的簡潔度。另外需要注意的是,編寫基於類或者生成器的上下文管理器時,記住不要忘記釋放資源。--THE END--

本篇文章就到這裡了,希望能夠給你帶來幫助,也希望您能夠多多關注it145.com的更多內容!


IT145.com E-mail:sddin#qq.com