with 语句是什么
with 是 Python 的上下文管理器语法,核心思想是:把”进入某个环境”和”离开时的清理工作”封装在一起,保证无论是否发生异常,清理逻辑都会被执行。
# 不用 with
f = open("file.txt")
data = f.read()
f.close() # 若上方抛异常,这行永远不会执行
# 用 with
with open("file.txt") as f:
data = f.read()
# 离开块时,f.close() 自动调用底层机制
with 依赖两个魔术方法:
class MyContext:
def __enter__(self):
return self # 绑定到 as 后面的变量
def __exit__(self, exc_type, exc_val, exc_tb):
# 无论是否异常都会执行
# 返回 True 表示吞掉异常,False/None 表示继续传播
return Falsewith 语句本质等价于:
f = open("file.txt")
try:
data = f.read()
finally:
f.__exit__(...) # 即 f.close()魔法不在 with,而在 finally 的语义保证 + 文件对象自己在 __exit__ 里写了 close()。
用 contextlib 简化写法
不想定义类时,用 @contextmanager 装饰器:
from contextlib import contextmanager
@contextmanager
def my_context():
print("进入")
try:
yield "资源对象" # yield 前 = __enter__,yield 后 = __exit__
finally:
print("离开")yield 把函数劈成两半,前半是 setup,后半是 teardown。
魔术方法
Python 里以双下划线开头和结尾的方法,如 __init__、__enter__、__len__。“魔术”在于:你不直接调用它们,Python 在特定语法触发时替你调用。
| 语法 | 实际调用 |
|---|---|
with obj | obj.__enter__() / obj.__exit__() |
for x in obj | obj.__iter__() / obj.__next__() |
len(obj) | obj.__len__() |
obj[key] | obj.__getitem__(key) |
obj + other | obj.__add__(other) |
可以把魔术方法理解为 Python 的隐式接口——接口名不是你定义的,是语言约定死的。
与 Go 接口的对比
同一个需求:“能发出声音的东西”。
Go:先定义接口,类型隐式满足
type Speaker interface {
Speak() string
}
type Dog struct{}
func (d Dog) Speak() string { return "Woof" }
func MakeNoise(s Speaker) {
fmt.Println(s.Speak())
}Python:靠约定的魔术方法,duck typing
class Dog:
def __str__(self):
return "Woof"
def make_noise(animal):
print(str(animal)) # 调用 animal.__str__()核心差异:
- Go 对接口编程,编译期检查,
MakeNoise明确要求Speaker。 - Python 的
make_noise对参数类型无要求,只要有__str__就能跑——这是鸭子类型:能叫就是鸭子。 - 代价是错误从编译期推迟到运行时。
两者思路相近——“有这个方法就能用”——只是切入点不同。Go 是显式接口声明,Python 是隐式魔术方法协议。