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 = filename
self.mode = mode
def __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 1
yield 2
def 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
。