Python中with的作用和用法

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

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


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

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

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

如果我們忘記釋放這些資源,可能會導致嚴重的問題,比如:

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

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

不安全的寫法:

f = open('my_file.txt', 'w')
f.write('hello world')
# 如果在 write 和 close 之間發生錯誤,close() 將永遠不會被執行!
f.close()

這個寫法非常危險。如果在 f.write() 時發生異常(例如磁盤滿了),程序會崩潰,f.close() 就不會被調用,文件資源就泄露了。

安全的、但繁瑣的寫法 (使用 try...finally):
為了確保資源一定被釋放,我們通常使用 try...finally 結構:

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

這個寫法是安全的,因為無論 try 塊中是否發生異常,finally 塊中的代碼都保證會被執行。但是,它看起來很冗長,代碼結構也不夠優雅。

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


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

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

基本語法:

with expression as variable:# 在這個代碼塊中,資源是可用的# ... do something with variable ...# 離開 with 代碼塊后,資源會自動被清理

使用 with 重寫文件操作:

with open('my_file.txt', 'w') as f:f.write('hello world')# 在這里可以進行各種文件操作# 比如 f.read(), f.writelines() 等# 當代碼執行離開這個 with 塊時(無論是正常結束還是發生異常),
# Python 會自動調用 f.close(),我們完全不需要操心。

對比一下:

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

with 語句的核心優勢是:無論 with 塊內部發生什么(即使是異常),它都保證能執行資源的“清理”操作


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

with 語句之所以能自動管理資源,是因為它遵循了上下文管理器協議(Context Manager Protocol)

一個對象只要實現了下面這兩個特殊方法,它就是一個上下文管理器:

  1. __enter__(self)

    • 何時調用:當進入 with 語句塊時,該方法被調用。
    • 作用:負責“獲取”資源或進行初始化設置。
    • 返回值:這個方法的返回值會賦給 as 后面的變量(如果 as 存在的話)。如果你不需要 as 變量,這個方法可以不返回任何東西。
  2. __exit__(self, exc_type, exc_value, traceback)

    • 何時調用:當離開 with 語句塊時(無論是正常退出還是因為異常退出),該方法被調用。
    • 作用:負責“釋放”資源或執行清理操作(比如 f.close())。
    • 參數
      • exc_type: 異常的類型(如果沒發生異常,則為 None)。
      • exc_value: 異常的值(如果沒發生異常,則為 None)。
      • traceback: 異常的追溯信息(如果沒發生異常,則為 None)。
    • 返回值
      • 如果 __exit__ 方法返回 True,表示它已經處理了這個異常,異常會被“吞掉”(suppress),程序不會向外拋出。
      • 如果它返回 FalseNone(默認情況),任何發生的異常都會在 __exit__ 執行完畢后被重新拋出。

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

# 1. 創建上下文管理器對象
manager = open('my_file.txt', 'w')# 2. 調用 __enter__ 方法,返回值賦給 f
f = manager.__enter__()# 3. 執行 with 塊中的代碼
try:f.write('hello world')
finally:# 4. 無論如何,都調用 __exit__ 方法進行清理# (這里簡單展示,實際會傳遞異常信息)manager.__exit__(None, None, None)

4. 如何創建自己的上下文管理器?

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

方式一:基于類的實現

我們可以寫一個類,并實現 __enter____exit__ 方法。

示例:一個簡單的計時器

import timeclass Timer:def __init__(self, name):self.name = namedef __enter__(self):print(f"計時器 '{self.name}' 開始...")self.start_time = time.time()# 這個類本身就是資源,所以返回 selfreturn self def __exit__(self, exc_type, exc_value, traceback):self.end_time = time.time()duration = self.end_time - self.start_timeprint(f"計時器 '{self.name}' 結束,耗時: {duration:.4f} 秒")# 如果有異常,這里可以記錄日志if exc_type:print(f"在 '{self.name}' 中發生了異常: {exc_value}")# 返回 False 或 None,讓異常正常拋出return False# 使用自定義的 Timer
with Timer("數據處理") as t:print("正在處理數據...")time.sleep(2)print("數據處理完成。")print("-" * 20)with Timer("有問題的操作") as t:print("準備執行一個會出錯的操作...")time.sleep(1)result = 1 / 0  # 這里會產生一個 ZeroDivisionErrorprint("這行代碼不會被執行")

輸出:

計時器 '數據處理' 開始...
正在處理數據...
數據處理完成。
計時器 '數據處理' 結束,耗時: 2.0021 秒
--------------------
計時器 '有問題的操作' 開始...
準備執行一個會出錯的操作...
計時器 '有問題的操作' 結束,耗時: 1.0011 秒
在 '有問題的操作' 中發生了異常: division by zero
Traceback (most recent call last):File "...", line 36, in <module>result = 1 / 0  # 這里會產生一個 ZeroDivisionError
ZeroDivisionError: division by zero

可以看到,即使發生了異常,__exit__ 方法仍然被調用,成功打印了耗時和異常信息。

方式二:基于生成器的實現(使用 contextlib 模塊)

對于簡單的上下文管理器,每次都寫一個類有點麻煩。Python 的 contextlib 模塊提供了一個 @contextmanager 裝飾器,可以讓我們用更簡潔的方式實現。

import time
from contextlib import contextmanager@contextmanager
def timer(name):print(f"計時器 '{name}' 開始...")start_time = time.time()# yield 之前的部分,相當于 __enter__# yield 的值會成為 as 后面的變量(如果沒有 yield 值,則為 None)try:yieldfinally:# yield 之后的部分,相當于 __exit__end_time = time.time()duration = end_time - start_timeprint(f"計時器 '{name}' 結束,耗時: {duration:.4f} 秒")# 使用方法完全一樣
with timer("數據處理_v2"):print("正在處理數據...")time.sleep(2)print("數據處理完成。")

這種方式更加 Pythonic,代碼也更緊湊。try...yield...finally 結構完美地對應了“進入-執行-清理”的模式。


總結

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

在現代 Python 編程中,只要遇到需要獲取和釋放資源的場景,都應該優先考慮使用 with 語句。

本文來自互聯網用戶投稿,該文觀點僅代表作者本人,不代表本站立場。本站僅提供信息存儲空間服務,不擁有所有權,不承擔相關法律責任。
如若轉載,請注明出處:http://www.pswp.cn/bicheng/89430.shtml
繁體地址,請注明出處:http://hk.pswp.cn/bicheng/89430.shtml
英文地址,請注明出處:http://en.pswp.cn/bicheng/89430.shtml

如若內容造成侵權/違法違規/事實不符,請聯系多彩編程網進行投訴反饋email:809451989@qq.com,一經查實,立即刪除!

相關文章

緩存雪崩、緩存穿透,緩存擊穿

Redis是一個完全開源免費的高性能非關系型&#xff08;NOSQL&#xff09;的key-value數據庫。 Redis不可能把所有的數據都緩存起來(內存昂貴且有限)&#xff0c;所以Redis需要對數據 設置過期時間&#xff0c;并采用的是惰性刪除定期刪除兩種策略對過期鍵刪除。Redis對過期鍵的…

springmvc跨域解決方案

在Spring MVC中處理跨域請求&#xff08;CORS&#xff0c;Cross-Origin Resource Sharing&#xff09;通常涉及到配置HTTP響應頭&#xff0c;以允許來自不同源的請求。Spring MVC提供了多種方式來配置CORS&#xff0c;包括全局配置和局部配置。 使用CrossOrigin注解 在控制器的…

btstack移植之安全配對(二)

3.13.3 Legacy配對首先&#xff0c;我們回復的paring response中&#xff0c;可以看到我們不支持secure connection&#xff0c;所以我們走的是legacy配對模式。圖3-74 secure連接不支持然后&#xff0c;master在pairing confirm包中回復了confirm value。圖3-75 master發送con…

FRP配置( CentOS 7 上安裝 FRP教程 )

** 如果你們公司沒有公網IP&#xff0c;但是又想實現內網穿透&#xff0c;遠程調用接口&#xff0c;在家也能調用公司服務器&#xff0c;但是nkg ssl有問題&#xff0c;花生殼坑壁&#xff0c;那么FRP是你最佳的選擇&#xff01;&#xff01;&#xff01;** 不過有個前提&#…

第三次mysql作業

建立庫建立mydb11_syu庫2.創建s表&#xff0c;創建sc表二&#xff0e;插入數據向s表插入數據2.向sc表插入數據三&#xff0e;查詢1.分別查詢student表和score表的所有記錄2.查詢student表的第2條到5條記錄3.從student表中查詢計算機系和英語系的學生的信息4.從student表中查詢年…

不同場景下git指令的搭配

添加賬號 git config --global user.name "YourName" git config --global user.email "your_emailexample.com"設置 Git 默認分支名稱為 main&#xff1a; git config --global init.defaultBranch main初始化倉庫&#xff1a; git init配置SSH 密鑰 如果…

NLP——遷移學習

一、遷移學習的概念 1.預訓練模型(Pretrained model) 定義: 簡單來說別人訓練好的模型。一般預訓練模型具備復雜的網絡模型結構&#xff1b;一般是在大量的語料下訓練完成的。 2.微調(Fine-tuning) 定義:一般是對預訓練語言模型&#xff0c;進行垂直領域數據的微調&#xff0c;…

Ubuntu 安裝

文章目錄硬件準備下載 Ubuntu 鏡像創建可啟動 USB從 USB 驅動器啟動安裝 Ubuntu不要忘記更新&#xff01;用了十多年的筆記本&#xff0c;手邊正好有個500G的固態&#xff0c;準備換上。考慮到機器的硬件配置&#xff0c;現在使用windows10實在是有點卡&#xff0c;ubuntu卻剛好…

【46】MFC入門到精通——MFC顯示實時時間,獲取系統當前時間GetCurrentTime()、獲取本地時間GetLocalTime()

文章目錄1 MFC獲取時間方法方法一&#xff1a;獲取系統當前時間GetCurrentTime()方法二&#xff1a;獲取本地時間GetLocalTime()使用GetTickCount()獲取程序運行時間2 MFC顯示實時時間 使用方法2.1 獲取時間2.2 類向導 添加定時器函數 OnTimer2.3 初始化 設置定時器2.4 定時器函…

Linux717 SWAP擴容;邏輯卷條帶化

root192.168.235.20s password:┌────────────────────────────────────────────────────────────────────┐│ ? MobaXterm 20.0 ? ││ …

人類社會發展過程中的熵增定律

引子研究美國羅斯福新政期間的法案為什么會對美國經濟恢復起作用&#xff1f;與千金買馬骨和移木賞金之間的區別與聯系&#xff1f;以下為豆包 AI回答一、羅斯福新政法案對美國經濟起作用的原因羅斯福新政&#xff08;1933-1939年&#xff09;通過一系列政策應對大蕭條&#x…

Spring-AI系列-入門篇-核心概念、組件和生產要素

原文-Spring AI知識庫&#xff0c;歡迎大家評論互動 師父領進門&#xff0c;修行靠自己。 Spring AI is an application framework for AI engineering. Its goal is to apply to the AI domain Spring ecosystem design principles such as portability and modular design an…

從瀏覽器到服務器:TCP 段的網絡傳輸之旅

本文以簡化的網絡架構為例&#xff0c;詳細介紹了當你在瀏覽器中輸入網址&#xff08;例如www.google.com&#xff09;并按下回車鍵后&#xff0c;TCP段的完整傳輸過程。我們將探討DNS解析、ARP、TCP/IP封裝、PAT和路由如何協同工作&#xff0c;將數據從個人電腦通過局域網和廣…

HCIE - 云計算拿下后的職業選擇如何規劃?

Hello&#xff01;大家好&#xff0c;小編是一名專注 IT 領域的資深探索家。我們聊聊HCIE - 云計算&#xff0c;這個認證作為華為認證體系中云計算領域的專家級認證&#xff0c;標志著持有者具備企業級云架構設計、復雜云平臺運維及跨場景技術落地能力。但認證本身只是職業進階…

1-創建Vue3項目

創建Vue3項目前提 已安裝 18.3 或更高版本的 Node.js vue 官網 https://vuejs.org/ 創建一個 Vue 應用 ① 新建項目目錄&#xff0c;使用 VSCode 打開 VSCode 可安裝 Vue-Official 插件協助開發 ② 執行 create vue 指令創建 vue 應用 npm create vuelatest這一指令將…

Codex,Copilot 是什么

Codex是什么 Codex 是 OpenAI 研發的一款專注于代碼生成的大型語言模型,它可以根據自然語言描述自動編寫程序代碼,在軟件開發、自動化測試等領域展現出了強大的應用潛力。下面為你詳細介紹: 1. 核心功能 代碼生成:Codex 能夠依據自然語言指令生成代碼,像函數、類或者完整…

Typecho插件開發:自定義表單驗證規則addRule實戰指南

文章目錄 Typecho表單驗證進階:為插件和主題添加自定義addRule驗證規則 引言 一、Typecho表單驗證基礎 1.1 Typecho表單系統概述 1.2 addRule方法解析 二、自定義驗證規則實現 2.1 創建自定義驗證類 2.2 注冊自定義驗證規則 2.3 使用自定義驗證規則 三、高級驗證場景實現 3.1 …

數據分布是如何影響目標檢測精度的

文章目錄一、研究背景與目標模型效果提升數據集優化二、研究問題明細各方向的關聯性與核心邏輯1. 高質量數據集的高效篩選與主動學習應用2. 基于推理結果的數據補充與增強方向優化3. 多類別場景下目標尺度與模型精度的關聯性4. 損失函數與數據增強對精度的量化影響5. 目標類型專…

Python 網絡爬蟲 —— 代理服務器

一、會話&#xff08;Session&#xff09;&#xff08;一&#xff09;核心邏輯HTTP 本身無記憶&#xff0c;每次請求獨立。會話&#xff08;Session&#xff09; 就是為解決這問題&#xff0c;讓客戶端&#xff08;瀏覽器&#xff09;和服務器 “記住” 交互狀態&#xff08;比…

Vue在線預覽Excel和Docx格式文件

前提&#xff1a;本次示例基于Vue2.x&#xff0c;所用插件為Vue-Office。 一、Vue-Office 插件簡介 Vue-Office 是一個一站式解決方案&#xff0c;支持多種 Office 文件格式的在線預覽&#xff0c;包括&#xff1a; Word&#xff08;.docx&#xff09;Excel&#xff08;.xlsx、…