欧美bbbwbbbw肥妇,免费乱码人妻系列日韩,一级黄片

Python中with的作用和使用解讀

 更新時(shí)間:2025年07月18日 08:57:59   作者:Tipriest_  
Python的with語句通過上下文管理器協(xié)議(__enter__和__exit__方法),簡化資源管理,確保文件、數(shù)據(jù)庫等資源在使用后自動(dòng)釋放,避免泄露,支持自定義實(shí)現(xiàn),提升代碼簡潔性與可讀性

在這里我們來詳細(xì)解釋一下Python中非常重要的 with 語句。

我會(huì)從 “為什么需要它” 開始,然后講解 “它是什么以及如何使用”,最后深入到 “它的工作原理”“如何自定義”

1. 為什么需要with語句?(The Problem)

在編程中,我們經(jīng)常會(huì)使用一些需要“獲取”和“釋放”的資源,比如:

  • 文件操作:打開文件后,必須記得關(guān)閉它。
  • 數(shù)據(jù)庫連接:建立連接后,必須記得關(guān)閉連接。
  • 線程鎖:獲取鎖之后,必須記得釋放它。

如果我們忘記釋放這些資源,可能會(huì)導(dǎo)致嚴(yán)重的問題,比如:

  • 文件句柄耗盡,無法再打開新文件。
  • 數(shù)據(jù)庫連接池被占滿,應(yīng)用無法再連接數(shù)據(jù)庫。
  • 線程死鎖,程序卡住。

讓我們看一個(gè)沒有 with 的文件操作例子:

不安全的寫法:

f = open('my_file.txt', 'w')
f.write('hello world')
# 如果在 write 和 close 之間發(fā)生錯(cuò)誤,close() 將永遠(yuǎn)不會(huì)被執(zhí)行!
f.close()

這個(gè)寫法非常危險(xiǎn)。如果在 f.write() 時(shí)發(fā)生異常(例如磁盤滿了),程序會(huì)崩潰,f.close() 就不會(huì)被調(diào)用,文件資源就泄露了。

安全的、但繁瑣的寫法 (使用 try...finally):

為了確保資源一定被釋放,我們通常使用 try...finally 結(jié)構(gòu):

f = None # 在 try 外面初始化,確保 finally 中可以訪問
try:
    f = open('my_file.txt', 'w')
    f.write('hello world')
    # ... 其他可能出錯(cuò)的操作 ...
finally:
    if f:
        f.close()

這個(gè)寫法是安全的,因?yàn)闊o論 try 塊中是否發(fā)生異常,finally 塊中的代碼都保證會(huì)被執(zhí)行。但是,它看起來很冗長,代碼結(jié)構(gòu)也不夠優(yōu)雅。

with 語句就是為了解決這個(gè)問題而生的,它能讓我們用更簡潔、更安全的方式來管理資源。

2.with語句是什么以及如何使用?(The Solution)

with 語句是一種上下文管理的語法糖(Syntactic Sugar)。它極大地簡化了上面 try...finally 的寫法。

基本語法:

with expression as variable:
    # 在這個(gè)代碼塊中,資源是可用的
    # ... do something with variable ...

# 離開 with 代碼塊后,資源會(huì)自動(dòng)被清理

使用 with 重寫文件操作:

with open('my_file.txt', 'w') as f:
    f.write('hello world')
    # 在這里可以進(jìn)行各種文件操作
    # 比如 f.read(), f.writelines() 等

# 當(dāng)代碼執(zhí)行離開這個(gè) with 塊時(shí)(無論是正常結(jié)束還是發(fā)生異常),
# Python 會(huì)自動(dòng)調(diào)用 f.close(),我們完全不需要操心。

對(duì)比一下:

  • try...finally 版本:5-6 行代碼,結(jié)構(gòu)復(fù)雜。
  • with 版本:2 行代碼,邏輯清晰,意圖明確(“在處理這個(gè)文件的上下文中,做這些事”)。

with 語句的核心優(yōu)勢是:無論 with 塊內(nèi)部發(fā)生什么(即使是異常),它都保證能執(zhí)行資源的“清理”操作。

3.with的工作原理:上下文管理器協(xié)議 (The Magic Behind)

with 語句之所以能自動(dòng)管理資源,是因?yàn)樗裱?strong>上下文管理器協(xié)議(Context Manager Protocol)。

一個(gè)對(duì)象只要實(shí)現(xiàn)了下面這兩個(gè)特殊方法,它就是一個(gè)上下文管理器:

__enter__(self)

  • 何時(shí)調(diào)用:當(dāng)進(jìn)入 with 語句塊時(shí),該方法被調(diào)用。
  • 作用:負(fù)責(zé)“獲取”資源或進(jìn)行初始化設(shè)置。
  • 返回值:這個(gè)方法的返回值會(huì)賦給 as 后面的變量(如果 as 存在的話)。如果你不需要 as 變量,這個(gè)方法可以不返回任何東西。

__exit__(self, exc_type, exc_value, traceback)

  • 何時(shí)調(diào)用:當(dāng)離開 with 語句塊時(shí)(無論是正常退出還是因?yàn)楫惓M顺觯?,該方法被調(diào)用。
  • 作用:負(fù)責(zé)“釋放”資源或執(zhí)行清理操作(比如 f.close())。

參數(shù)

  • exc_type: 異常的類型(如果沒發(fā)生異常,則為 None)。
  • exc_value: 異常的值(如果沒發(fā)生異常,則為 None)。
  • traceback: 異常的追溯信息(如果沒發(fā)生異常,則為 None)。

返回值

  • 如果 __exit__ 方法返回 True,表示它已經(jīng)處理了這個(gè)異常,異常會(huì)被“吞掉”(suppress),程序不會(huì)向外拋出。
  • 如果它返回 FalseNone(默認(rèn)情況),任何發(fā)生的異常都會(huì)在 __exit__ 執(zhí)行完畢后被重新拋出。

所以,with open(...) as f: 這段代碼大致等同于下面的偽代碼:

# 1. 創(chuàng)建上下文管理器對(duì)象
manager = open('my_file.txt', 'w')

# 2. 調(diào)用 __enter__ 方法,返回值賦給 f
f = manager.__enter__()

# 3. 執(zhí)行 with 塊中的代碼
try:
    f.write('hello world')
finally:
    # 4. 無論如何,都調(diào)用 __exit__ 方法進(jìn)行清理
    # (這里簡單展示,實(shí)際會(huì)傳遞異常信息)
    manager.__exit__(None, None, None)

4. 如何創(chuàng)建自己的上下文管理器?

了解了原理,我們就可以創(chuàng)建自己的上下文管理器。有兩種主要方式:

方式一:基于類的實(shí)現(xiàn)

我們可以寫一個(gè)類,并實(shí)現(xiàn) __enter____exit__ 方法。

示例:一個(gè)簡單的計(jì)時(shí)器

import time

class Timer:
    def __init__(self, name):
        self.name = name

    def __enter__(self):
        print(f"計(jì)時(shí)器 '{self.name}' 開始...")
        self.start_time = time.time()
        # 這個(gè)類本身就是資源,所以返回 self
        return self 

    def __exit__(self, exc_type, exc_value, traceback):
        self.end_time = time.time()
        duration = self.end_time - self.start_time
        print(f"計(jì)時(shí)器 '{self.name}' 結(jié)束,耗時(shí): {duration:.4f} 秒")
        # 如果有異常,這里可以記錄日志
        if exc_type:
            print(f"在 '{self.name}' 中發(fā)生了異常: {exc_value}")
        # 返回 False 或 None,讓異常正常拋出
        return False

# 使用自定義的 Timer
with Timer("數(shù)據(jù)處理") as t:
    print("正在處理數(shù)據(jù)...")
    time.sleep(2)
    print("數(shù)據(jù)處理完成。")

print("-" * 20)

with Timer("有問題的操作") as t:
    print("準(zhǔn)備執(zhí)行一個(gè)會(huì)出錯(cuò)的操作...")
    time.sleep(1)
    result = 1 / 0  # 這里會(huì)產(chǎn)生一個(gè) ZeroDivisionError
    print("這行代碼不會(huì)被執(zhí)行")

輸出:

計(jì)時(shí)器 '數(shù)據(jù)處理' 開始...
正在處理數(shù)據(jù)...
數(shù)據(jù)處理完成。
計(jì)時(shí)器 '數(shù)據(jù)處理' 結(jié)束,耗時(shí): 2.0021 秒
--------------------
計(jì)時(shí)器 '有問題的操作' 開始...
準(zhǔn)備執(zhí)行一個(gè)會(huì)出錯(cuò)的操作...
計(jì)時(shí)器 '有問題的操作' 結(jié)束,耗時(shí): 1.0011 秒
在 '有問題的操作' 中發(fā)生了異常: division by zero
Traceback (most recent call last):
  File "...", line 36, in <module>
    result = 1 / 0  # 這里會(huì)產(chǎn)生一個(gè) ZeroDivisionError
ZeroDivisionError: division by zero

可以看到,即使發(fā)生了異常,__exit__ 方法仍然被調(diào)用,成功打印了耗時(shí)和異常信息。

方式二:基于生成器的實(shí)現(xiàn)(使用contextlib模塊)

對(duì)于簡單的上下文管理器,每次都寫一個(gè)類有點(diǎn)麻煩。

Python 的 contextlib 模塊提供了一個(gè) @contextmanager 裝飾器,可以讓我們用更簡潔的方式實(shí)現(xiàn)。

import time
from contextlib import contextmanager

@contextmanager
def timer(name):
    print(f"計(jì)時(shí)器 '{name}' 開始...")
    start_time = time.time()
    
    # yield 之前的部分,相當(dāng)于 __enter__
    # yield 的值會(huì)成為 as 后面的變量(如果沒有 yield 值,則為 None)
    try:
        yield
    finally:
        # yield 之后的部分,相當(dāng)于 __exit__
        end_time = time.time()
        duration = end_time - start_time
        print(f"計(jì)時(shí)器 '{name}' 結(jié)束,耗時(shí): {duration:.4f} 秒")

# 使用方法完全一樣
with timer("數(shù)據(jù)處理_v2"):
    print("正在處理數(shù)據(jù)...")
    time.sleep(2)
    print("數(shù)據(jù)處理完成。")

這種方式更加 Pythonic,代碼也更緊湊。try...yield...finally 結(jié)構(gòu)完美地對(duì)應(yīng)了“進(jìn)入-執(zhí)行-清理”的模式。

總結(jié)

  • 用途with 語句用于自動(dòng)管理資源,確保資源在使用完畢后(無論是否發(fā)生異常)都能被正確清理。
  • 優(yōu)點(diǎn):代碼更簡潔、更安全、更具可讀性,避免了冗長的 try...finally 結(jié)構(gòu)和資源泄露的風(fēng)險(xiǎn)。
  • 原理:依賴于上下文管理器協(xié)議,即對(duì)象需實(shí)現(xiàn) __enter__()__exit__() 兩個(gè)方法。
  • 自定義:你可以通過編寫類或使用 contextlib.contextmanager 裝飾器來創(chuàng)建自己的上下文管理器,封裝任何需要“設(shè)置-清理”邏輯的場景。

在現(xiàn)代 Python 編程中,只要遇到需要獲取和釋放資源的場景,都應(yīng)該優(yōu)先考慮使用 with 語句。

以上為個(gè)人經(jīng)驗(yàn),希望能給大家一個(gè)參考,也希望大家多多支持腳本之家。

相關(guān)文章

  • 簡單解決Python文件中文編碼問題

    簡單解決Python文件中文編碼問題

    這篇文章主要介紹了簡單解決Python文件中文編碼問題的相關(guān)資料,需要的朋友可以參考下
    2015-11-11
  • 詳解Python同時(shí)寫入多個(gè)文件的5種方法

    詳解Python同時(shí)寫入多個(gè)文件的5種方法

    在實(shí)際開發(fā)中,有同學(xué)經(jīng)常問田辛老師需要將數(shù)據(jù)同時(shí)寫入多個(gè)文件的場景,Python提供了多種高效且安全的方法來實(shí)現(xiàn)這一需求,下面小編就來和大家簡單講講吧
    2025-05-05
  • pyqt6的本地環(huán)境部署(conda和vscode環(huán)境)

    pyqt6的本地環(huán)境部署(conda和vscode環(huán)境)

    本文主要介紹了pyqt6的本地環(huán)境部署(conda和vscode環(huán)境),文中通過示例介紹的非常詳細(xì),對(duì)大家的學(xué)習(xí)或者工作具有一定的參考學(xué)習(xí)價(jià)值,需要的朋友們下面隨著小編來一起學(xué)習(xí)學(xué)習(xí)吧
    2025-05-05
  • pytorch中forwod函數(shù)在父類中的調(diào)用方式解讀

    pytorch中forwod函數(shù)在父類中的調(diào)用方式解讀

    這篇文章主要介紹了pytorch中forwod函數(shù)在父類中的調(diào)用方式解讀,具有很好的參考價(jià)值,希望對(duì)大家有所幫助。如有錯(cuò)誤或未考慮完全的地方,望不吝賜教
    2023-02-02
  • Python matplotlib繪制實(shí)時(shí)數(shù)據(jù)動(dòng)畫

    Python matplotlib繪制實(shí)時(shí)數(shù)據(jù)動(dòng)畫

    Matplotlib作為Python的2D繪圖庫,它以各種硬拷貝格式和跨平臺(tái)的交互式環(huán)境生成出版質(zhì)量級(jí)別的圖形。本文將利用Matplotlib庫繪制實(shí)時(shí)數(shù)據(jù)動(dòng)畫,感興趣的可以了解一下
    2022-03-03
  • Python計(jì)算標(biāo)準(zhǔn)差之numpy.std和torch.std的區(qū)別

    Python計(jì)算標(biāo)準(zhǔn)差之numpy.std和torch.std的區(qū)別

    Torch自稱為神經(jīng)網(wǎng)絡(luò)中的numpy,它會(huì)將torch產(chǎn)生的tensor放在GPU中加速運(yùn)算,就像numpy會(huì)把a(bǔ)rray放在CPU中加速運(yùn)算,下面這篇文章主要給大家介紹了關(guān)于Python?Numpy計(jì)算標(biāo)準(zhǔn)差之numpy.std和torch.std區(qū)別的相關(guān)資料,需要的朋友可以參考下
    2022-08-08
  • Python實(shí)現(xiàn)交通數(shù)據(jù)可視化的示例代碼

    Python實(shí)現(xiàn)交通數(shù)據(jù)可視化的示例代碼

    本文主要分享了Python交通數(shù)據(jù)分析與可視化的實(shí)戰(zhàn)!其中主要是使用TransBigData庫快速高效地處理、分析、挖掘出租車GPS數(shù)據(jù),感興趣的可以了解一下
    2023-04-04
  • python  Django中的apps.py的目的是什么

    python Django中的apps.py的目的是什么

    這篇文章主要介紹了python Django中的apps.py的目的是什么,本文通過實(shí)例代碼給大家介紹的非常詳細(xì),需要的朋友可以參考下
    2018-10-10
  • python貪吃蛇核心功能實(shí)現(xiàn)下

    python貪吃蛇核心功能實(shí)現(xiàn)下

    我想大家都玩過諾基亞上面的貪吃蛇吧,這篇文章將帶你一步步用python語言實(shí)現(xiàn)一個(gè)snake小游戲,文中的示例代碼講解詳細(xì),感興趣的可以了解一下
    2022-09-09
  • Python字符串格式化輸出代碼實(shí)例

    Python字符串格式化輸出代碼實(shí)例

    這篇文章主要介紹了Python字符串格式化輸出代碼實(shí)例,文中通過示例代碼介紹的非常詳細(xì),對(duì)大家的學(xué)習(xí)或者工作具有一定的參考學(xué)習(xí)價(jià)值,需要的朋友可以參考下
    2019-11-11

最新評(píng)論