詳解Python中的with語句和上下文管理器
如果你有閱讀源碼的習(xí)慣,可能會看到一些優(yōu)秀的代碼經(jīng)常出現(xiàn)帶有 with 關(guān)鍵字的語句,它通常用在什么場景呢?對于系統(tǒng)資源如文件、數(shù)據(jù)庫連接、socket 而言,應(yīng)用程序打開這些資源并執(zhí)行完業(yè)務(wù)邏輯之后,必須做的一件事就是要關(guān)閉(斷開)該資源。
比如 Python 程序打開一個文件,往文件中寫內(nèi)容,寫完之后,就要關(guān)閉該文件,否則會出現(xiàn)什么情況呢?極端情況下會出現(xiàn) Too many open files 的錯誤,因為系統(tǒng)允許你打開的最大文件數(shù)量是有限的。同樣,對于數(shù)據(jù)庫,如果連接數(shù)過多而沒有及時關(guān)閉的話,就可能會出現(xiàn) Can not connect to MySQL server Too many connections,因為數(shù)據(jù)庫連接是一種非常昂貴的資源,不可能無限制的被創(chuàng)建。
一、with語句的使用
向文件中寫入數(shù)據(jù)的示例代碼(基礎(chǔ)):
# -*- coding: utf-8 -*-
# @Time : 2022-10-18 10:25
# @Author : AmoXiang
# @File : demo1.py
# @Software: PyCharm
# @Blog : https://blog.csdn.net/xw1680
# 1、以寫的方式打開文件
f = open("1.txt", "w", encoding="utf8")
# 2、寫入文件內(nèi)容
f.write("hello world")
# 3、關(guān)閉文件
f.close()
代碼說明如下:文件使用完后必須關(guān)閉,因為文件對象會占用操作系統(tǒng)的資源,并且操作系統(tǒng)同一時間能打開的文件數(shù)量也是有限的。這種寫法可能出現(xiàn)一定的安全隱患,錯誤代碼如下:
# -*- coding: utf-8 -*-
# @Time : 2022-10-18 10:25
# @Author : AmoXiang
# @File : demo1.py
# @Software: PyCharm
# @Blog : https://blog.csdn.net/xw1680
# 1、以寫的方式打開文件
f = open("1.txt", "r", encoding="utf8")
# 2、寫入文件內(nèi)容
f.write("hello world")
# 3、關(guān)閉文件
f.close()
運(yùn)行結(jié)果如下圖所示:

代碼說明:由于文件讀寫時都有可能產(chǎn)生 IOError,一旦出錯,后面的 f.close() 就不會調(diào)用。為了保證無論是否出錯都能正確地關(guān)閉文件,我們可以使用 try ... finally 來解決。安全寫法, 代碼如下:
# -*- coding: utf-8 -*-
# @Time : 2022-10-18 10:32
# @Author : AmoXiang
# @File : demo2.py
# @Software: PyCharm
# @Blog : https://blog.csdn.net/xw1680
try:
# 1、以讀的方式打開文件
f = open("1.txt", "r")
# 2、讀取文件內(nèi)容
f.write("xxxxx")
except IOError as e:
print("文件操作出錯", e)
finally:
# 3、關(guān)閉文件
f.close()
運(yùn)行結(jié)果:
文件操作出錯 not writable
這種方法雖然代碼運(yùn)行良好,但是缺點就是代碼過于冗長,并且需要添加 try-except-finally 語句,不是很方便,也容易忘記。在這種情況下,Python 提供了 with 語句的這種寫法,既簡單又安全,并且 with 語句執(zhí)行完成以后自動調(diào)用關(guān)閉文件操作,即使出現(xiàn)異常也會自動調(diào)用關(guān)閉文件操作。with 語句的示例代碼:
# -*- coding: utf-8 -*-
# @Time : 2022-10-18 10:34
# @Author : AmoXiang
# @File : demo3.py
# @Software: PyCharm
# @Blog : https://blog.csdn.net/xw1680
# 1、以寫的方式打開文件
with open("1.txt", "w") as f:
# 2、讀取文件內(nèi)容
f.write("hello world")
f.read() # 即使報錯,文件資源也會關(guān)閉掉
二、上下文管理器
一個類只要實現(xiàn)了 __enter__() 和 __exit__() 這個兩個方法,通過該類創(chuàng)建的對象我們就稱之為上下文管理器對象。上下文管理器可以使用 with 語句,with 語句之所以這么強(qiáng)大,背后是由上下文管理器做支撐的,也就是說剛才使用 open 函數(shù)創(chuàng)建的文件對象就是就是一個上下文管理器對象。自定義上下文管理器類,模擬文件操作。定義一個 File 類,實現(xiàn) __enter__() 和 __exit__() 方法,然后使用 with 語句來完成操作文件, 示例代碼:
# -*- coding: utf-8 -*-
# @Time : 2022-10-18 10:38
# @Author : AmoXiang
# @File : demo4.py
# @Software: PyCharm
# @Blog : https://blog.csdn.net/xw1680
class File(object):
# 初始化方法
def __init__(self, file_name, mode):
# 定義變量保存文件名和打開模式
self.file_name = file_name
self.mode = mode
# 上文方法
def __enter__(self):
print("進(jìn)入上文方法")
# 返回文件資源
self.f = open(self.file_name, self.mode)
return self.f
# 下文方法
def __exit__(self, exc_type, exc_val, exc_tb):
"""
with 語句中,即使發(fā)生異常信息,也會進(jìn)入 __exit__ 中
:param exc_type: 發(fā)生異常時,異常的類型
:param exc_val: 發(fā)生異常時,異常的信息
:param exc_tb: 異常對象,堆棧信息
:return:如果返回False(代碼沒有書寫返回值默認(rèn)None,表示為False) 代表
異常發(fā)生以后,異常會繼續(xù)向外傳遞。如果返回True,代表異常不再向外傳遞
"""
if exc_type:
print("發(fā)生了異常......")
return True
print("進(jìn)入下文方法")
self.f.close()
if __name__ == '__main__':
with File("1.txt", "r") as file:
content = file.read()
print(content)
運(yùn)行結(jié)果:

代碼說明:
__enter__表示上文方法,需要返回一個操作文件對象__exit__表示下文方法,with 語句執(zhí)行完成會自動執(zhí)行,即使出現(xiàn)異常也會執(zhí)行該方法。
上下文管理器的另外一種實現(xiàn)方式: 假如想要讓一個函數(shù)成為上下文管理器,Python 還提供了一個 @contextmanager 的裝飾器,更進(jìn)一步簡化了上下文管理器的實現(xiàn)方式。通過 yield 將函數(shù)分割成兩部分,yield 上面的語句在 __enter__ 方法中執(zhí)行,yield 下面的語句在 __exit__ 方法中執(zhí)行,緊跟在 yield 后面的參數(shù)是函數(shù)的返回值。示例代碼:
# -*- coding: utf-8 -*-
# @Time : 2022-10-18 10:54
# @Author : AmoXiang
# @File : demo5.py
# @Software: PyCharm
# @Blog : https://blog.csdn.net/xw1680
# 導(dǎo)入裝飾器
from contextlib import contextmanager
# 裝飾器裝飾函數(shù),讓其稱為一個上下文管理器對象
@contextmanager
def my_open(path, mode):
try:
# 打開文件
file = open(path, mode)
# yield之前的代碼好比是上文方法
yield file
except Exception as e:
print(e)
finally:
print("over")
# yield下面的代碼好比是下文方法
file.close()
# 使用with語句
with my_open('out.txt', 'w') as f:
f.write("hello , the simplest context manager")
三、小結(jié)
Python 提供了 with 語句 用于簡化資源釋放的操作,使用 with 語句 操作建立在上下文管理器(實現(xiàn) __enter__ 和 __exit__ ) 的基礎(chǔ)上。Python 還提供了一個 @contextmanager 裝飾器,更進(jìn)一步簡化上下管理器的實現(xiàn),讓一個函數(shù)可以成為上下文管理器,結(jié)合 with 語句 來使用。
到此這篇關(guān)于詳解Python中的with語句和上下文管理器的文章就介紹到這了,更多相關(guān)Python with語句和上下文管理器內(nèi)容請搜索腳本之家以前的文章或繼續(xù)瀏覽下面的相關(guān)文章希望大家以后多多支持腳本之家!
相關(guān)文章
pandas 如何保存數(shù)據(jù)到excel,csv
這篇文章主要介紹了pandas 如何保存數(shù)據(jù)到excel,csv的實現(xiàn)方式,具有很好的參考價值,希望對大家有所幫助。如有錯誤或未考慮完全的地方,望不吝賜教2022-07-07
使用python+requests+pytest實現(xiàn)接口自動化
這篇文章主要介紹了使用python+requests+pytest實現(xiàn)接口自動化,在當(dāng)前互聯(lián)網(wǎng)產(chǎn)品迭代頻繁的背景下,回歸測試的時間越來越少,但接口自動化測試因其實現(xiàn)簡單、維護(hù)成本低,容易提高覆蓋率等特點,越來越受重視,需要的朋友可以參考下2023-08-08
python-web根據(jù)元素屬性進(jìn)行定位的方法
這篇文章主要介紹了python-web根據(jù)元素屬性進(jìn)行定位的方法,本文給大家介紹的非常詳細(xì),具有一定的參考借鑒價值,需要的朋友可以參考下2019-12-12
python3通過gevent.pool限制協(xié)程并發(fā)數(shù)量的實現(xiàn)方法
這篇文章主要介紹了python3通過gevent.pool限制協(xié)程并發(fā)數(shù)量的實現(xiàn)方法,本文通過實例代碼給大家介紹的非常詳細(xì),對大家的學(xué)習(xí)或工作具有一定的參考借鑒價值,需要的朋友可以參考下2021-09-09
Python pyecharts實現(xiàn)繪制中國地圖的實例詳解
pyecharts是一個用于生成 Echarts 圖表的類庫。Echarts 是百度開源的一個數(shù)據(jù)可視化 JS 庫。用 Echarts 生成的圖可視化效果非常棒。本文將通過pyecharts繪制中國地圖,需要的可以學(xué)習(xí)一下2022-01-01

