網站首頁 編程語言 正文
如果你有閱讀源碼的習慣,可能會看到一些優秀的代碼經常出現帶有 with
關鍵字的語句,它通常用在什么場景呢?對于系統資源如文件、數據庫連接、socket 而言,應用程序打開這些資源并執行完業務邏輯之后,必須做的一件事就是要關閉(斷開)該資源。
比如 Python 程序打開一個文件,往文件中寫內容,寫完之后,就要關閉該文件,否則會出現什么情況呢?極端情況下會出現 Too many open files
的錯誤,因為系統允許你打開的最大文件數量是有限的。同樣,對于數據庫,如果連接數過多而沒有及時關閉的話,就可能會出現 Can not connect to MySQL server Too many connections
,因為數據庫連接是一種非常昂貴的資源,不可能無限制的被創建。
一、with語句的使用
向文件中寫入數據的示例代碼(基礎):
# -*- 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、寫入文件內容 f.write("hello world") # 3、關閉文件 f.close()
代碼說明如下:文件使用完后必須關閉,因為文件對象會占用操作系統的資源,并且操作系統同一時間能打開的文件數量也是有限的。這種寫法可能出現一定的安全隱患,錯誤代碼如下:
# -*- 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、寫入文件內容 f.write("hello world") # 3、關閉文件 f.close()
運行結果如下圖所示:
代碼說明:由于文件讀寫時都有可能產生 IOError
,一旦出錯,后面的 f.close()
就不會調用。為了保證無論是否出錯都能正確地關閉文件,我們可以使用 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、讀取文件內容 f.write("xxxxx") except IOError as e: print("文件操作出錯", e) finally: # 3、關閉文件 f.close()
運行結果:
文件操作出錯 not writable
這種方法雖然代碼運行良好,但是缺點就是代碼過于冗長,并且需要添加 try-except-finally
語句,不是很方便,也容易忘記。在這種情況下,Python 提供了 with 語句的這種寫法,既簡單又安全,并且 with 語句執行完成以后自動調用關閉文件操作,即使出現異常也會自動調用關閉文件操作。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、讀取文件內容 f.write("hello world") f.read() # 即使報錯,文件資源也會關閉掉
二、上下文管理器
一個類只要實現了 __enter__()
和 __exit__()
這個兩個方法,通過該類創建的對象我們就稱之為上下文管理器對象。上下文管理器可以使用 with 語句,with 語句之所以這么強大,背后是由上下文管理器做支撐的,也就是說剛才使用 open 函數創建的文件對象就是就是一個上下文管理器對象。自定義上下文管理器類,模擬文件操作。定義一個 File 類,實現 __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("進入上文方法") # 返回文件資源 self.f = open(self.file_name, self.mode) return self.f # 下文方法 def __exit__(self, exc_type, exc_val, exc_tb): """ with 語句中,即使發生異常信息,也會進入 __exit__ 中 :param exc_type: 發生異常時,異常的類型 :param exc_val: 發生異常時,異常的信息 :param exc_tb: 異常對象,堆棧信息 :return:如果返回False(代碼沒有書寫返回值默認None,表示為False) 代表 異常發生以后,異常會繼續向外傳遞。如果返回True,代表異常不再向外傳遞 """ if exc_type: print("發生了異常......") return True print("進入下文方法") self.f.close() if __name__ == '__main__': with File("1.txt", "r") as file: content = file.read() print(content)
運行結果:
代碼說明:
-
__enter__
表示上文方法,需要返回一個操作文件對象 -
__exit__
表示下文方法,with 語句執行完成會自動執行,即使出現異常也會執行該方法。
上下文管理器的另外一種實現方式: 假如想要讓一個函數成為上下文管理器,Python 還提供了一個 @contextmanager
的裝飾器,更進一步簡化了上下文管理器的實現方式。通過 yield 將函數分割成兩部分,yield 上面的語句在 __enter__
方法中執行,yield 下面的語句在 __exit__
方法中執行,緊跟在 yield 后面的參數是函數的返回值。示例代碼:
# -*- coding: utf-8 -*- # @Time : 2022-10-18 10:54 # @Author : AmoXiang # @File : demo5.py # @Software: PyCharm # @Blog : https://blog.csdn.net/xw1680 # 導入裝飾器 from contextlib import contextmanager # 裝飾器裝飾函數,讓其稱為一個上下文管理器對象 @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")
三、小結
Python 提供了 with 語句 用于簡化資源釋放的操作,使用 with 語句 操作建立在上下文管理器(實現 __enter__
和 __exit__
) 的基礎上。Python 還提供了一個 @contextmanager
裝飾器,更進一步簡化上下管理器的實現,讓一個函數可以成為上下文管理器,結合 with 語句 來使用。
原文鏈接:https://blog.csdn.net/xw1680/article/details/127382934
相關推薦
- 2022-11-02 python?pip特殊用法之pip?install?-v?-e?.命令詳解_python
- 2023-12-09 SpringBoot自定義異常處理機制
- 2022-11-09 linphone-sdk-android版本號生成解析_Android
- 2023-02-14 解決React報錯React.Children.only?expected?to?receive?s
- 2022-05-29 .NET?Core中的HttpClientFactory類用法詳解_實用技巧
- 2023-05-29 Python中如何給字典設置默認值_python
- 2022-10-13 解析React中useMemo與useCallback的區別_React
- 2022-04-17 python讀取文件夾下所有文件
- 最近更新
-
- window11 系統安裝 yarn
- 超詳細win安裝深度學習環境2025年最新版(
- Linux 中運行的top命令 怎么退出?
- MySQL 中decimal 的用法? 存儲小
- get 、set 、toString 方法的使
- @Resource和 @Autowired注解
- Java基礎操作-- 運算符,流程控制 Flo
- 1. Int 和Integer 的區別,Jav
- spring @retryable不生效的一種
- Spring Security之認證信息的處理
- Spring Security之認證過濾器
- Spring Security概述快速入門
- Spring Security之配置體系
- 【SpringBoot】SpringCache
- Spring Security之基于方法配置權
- redisson分布式鎖中waittime的設
- maven:解決release錯誤:Artif
- restTemplate使用總結
- Spring Security之安全異常處理
- MybatisPlus優雅實現加密?
- Spring ioc容器與Bean的生命周期。
- 【探索SpringCloud】服務發現-Nac
- Spring Security之基于HttpR
- Redis 底層數據結構-簡單動態字符串(SD
- arthas操作spring被代理目標對象命令
- Spring中的單例模式應用詳解
- 聊聊消息隊列,發送消息的4種方式
- bootspring第三方資源配置管理
- GIT同步修改后的遠程分支