PEP 343 – The “with” Statement,with 語句
這玩意讓我想起了Kotlin和Rust的問號標識符,都是將try-catch進行包裝,避免出現太多重復代碼(Go:我假設你不是在內涵我)
用法
最常見的用法就是對文件的操作,比如打開一個圖片文件,將其轉為 base64 編碼:
with open(image_path, "rb") as image_file:encoded_string = base64.b64encode(image_file.read()).decode()
原理
with
的本質是將如下代碼:
with EXPR as VAR:BLOCK
轉為如下代碼:
VAR = EXPR
try:VAR.__enter__()BLOCK
finally:VAR.__exit__()
也就是說,剛才的代碼可以重寫為:
image_file = open(image_path, "rb")try:file_obj = image_file.__enter__() # 手動調用 __enter__encoded_string = base64.b64encode(file_obj.read()).decode()finally:suppress_exception = image_file.__exit__(None, None, None) # 手動調用 __exit__
諸多細節不需要暴露在外,所以顯然使用with會讓代碼變得簡單。另一方面,如果你的代碼實現了__enter__()
和__exit__(self, exc_type, exc_val, exc_tb)
這兩個接口,你也可以使用with
比如如下代碼,我實現了一個自定義的文件管理器:
class FileManager:def __init__(self, filename, mode):self.filename = filenameself.mode = modedef __enter__(self):self.file = open(self.filename, self.mode)return self.file # 返回值賦給 as 后的變量def __exit__(self, exc_type, exc_val, exc_tb):self.file.close() # 確保文件關閉return False # 異常會繼續傳播# 使用自定義管理器
with FileManager("test.txt", "w") as f:f.write("Hello, World!") # 文件會自動關閉
PEP 380 – Syntax for Delegating to a Subgenerator,yield from 語法
簡單來說就是針對yield語法的補強。
在沒有 yield from
的情況下,需要手動循環并轉發值。如果有的話,對于鏈式轉發能節省一些代碼:
def subgenerator():yield 1yield 2def main_generator():for value in subgenerator():yield value # 手動轉發每個值# 使用 yield from 簡化:
def main_generator():yield from subgenerator() # 自動轉發所有值
PEP 405 – Python Virtual Environments,虛擬環境
一個新的python項目總伴隨著一大堆不同的依賴,不少依賴還不能向后兼容(這是我不喜歡python的一點)。虛擬環境的意義就是對當前項目需要的依賴單獨下載在一個目錄,這樣安裝的依賴就不會干擾系統級別python的依賴了。(題外話,一種觀點認為優秀的程序員需要認可虛擬化機制,比如虛擬機、docker容器和這次的python venv,我認為見仁見智吧)
Python 3.3+ 內置了 venv
模塊,用于創建虛擬環境:
python -m venv myenv # myenv 是環境名稱,可自定義
在Windows下激活虛擬環境:
myenv\Scripts\activate
Linux:
source myenv/bin/activate
激活環境以后,pip維護的包就僅限在該環境生效了。
需要指出一點是,虛擬環境默認不會繼承原來python環境的包,除非手動指定使其繼承全局包:
# 創建繼承全局包的虛擬環境
python -m venv --system-site-packages myenv
這樣并不推薦,因為這使得虛擬環境失去了純潔性。這個特性也意味著你可以在虛擬環境和全局環境安裝同一個包的兩個不同版本。
另外,一些核心的包,比如pip
等,會默認繼承到虛擬環境里。
python自帶的venv
還有一個問題就是,它無法對python自己進行虛擬化,一切虛擬化行為都在同一個python版本下。所以如果你有安裝多個python版本的需求,建議用uv
或者conda
。