一、with ... as ...是固定配套语法,缺一不可
with 后面必须接一个上下文管理器,这个管理器包含(__enter__ / __exit__)
with 语法的底层机制完全依赖enter+exit;只要对象实现这两个方法,就能作为 with 的参数。Session() 和 .begin() 只是返回了两种逻辑不同的管理器实例。
with是上下文管理器关键字,as用来接收上下文返回的对象,标准写法一定成对出现,少数极简写法可以省略as(不需要拿到对象时):
# 有as:拿到session对象(你代码里的场景)withSessionLocal.begin()assession:session.add(...)# 无as:只需要自动释放资源,不用操作对象withopen("a.txt"):print("文件自动打开关闭")二、核心作用:自动管理资源,自动清理(不用手动写try-finally)
with底层依赖上下文管理器协议(__enter__/__exit__),进入代码块自动执行__enter__,退出时无论是否报错都会执行__exit__释放资源。
常见使用场景(全部是需要「用完必须关闭/回滚/释放」的资源)
1. 数据库连接/会话(你写的SQLAlchemy场景)
# SessionLocal.begin() 返回事务上下文withSessionLocal.begin()assession:session.add(user)# 代码块正常走完:自动commit提交事务# 代码块抛异常:自动rollback回滚事务# 离开块自动关闭session,不用手动 session.close()不用with的原生写法,必须手动处理异常、提交、关闭,非常啰嗦:
session=SessionLocal()try:session.begin()session.add(user)session.commit()exceptException:session.rollback()finally:session.close()2. 文件读写(最经典例子)
自动关闭文件,避免文件句柄泄露:
withopen("test.txt","r",encoding="utf-8")asf:data=f.read()# 离开with自动f.close()3. 网络连接:socket、requests会话、数据库游标
importpsycopg2withpsycopg2.connect(...)asconn:withconn.cursor()ascur:cur.execute("SELECT 1")# 自动关闭游标、断开连接4. 锁、线程同步资源(threading.Lock)
自动加锁、释放锁,防止死锁:
importthreading lock=threading.Lock()withlock:# 自动acquire加锁print("临界区代码")# 离开自动release解锁5. 临时修改环境、恢复状态
比如临时改日志级别、临时切换目录,退出自动还原:
importoswithos.chdir("/tmp"):print(os.getcwd())# 自动切回原来工作目录6. 自定义上下文管理器
自己实现__enter__/__exit__或者用@contextmanager装饰器封装资源逻辑。
三、with SessionLocal.begin() as session
SQLAlchemy 的begin()是事务上下文管理器:
- 进入
with:开启数据库事务 - 块内正常执行完毕:自动
commit - 块内任意代码抛出异常:自动
rollback - 块结束:自动关闭会话,释放数据库连接回连接池
如果不用with,一旦中间代码报错忘记写rollback()/close(),会导致连接泄漏、事务卡死。
四、补充区分:两种with写法
- 需要操作对象 →
with xxx as obj:(90%场景,你代码属于这种) - 只需要自动释放、不用操作对象 →
with xxx:(极少用)
五、一句话总结
with和as是配套语法,绝大多数场景都要一起写;- 只要资源必须手动关闭/回滚/释放,就优先用
with as; - 核心优势:自动清理资源、异常安全,替代冗长的
try...finally。
zip
核心本质:zip迭代时产出元组
1. 基础行为
zip(*iterables)会并行遍历所有传入可迭代对象,按位置配对,每一组打包成 tuple:
a=[1,2,3]b=["x","y","z"]res=zip(a,b)foriteminres:print(item)# (1, 'x')# (2, 'y')# (3, 'z')# item 全部是元组 tuple2. 关键特性补充
- 长度以最短序列为准
多余元素直接丢弃:
zip([1,2],["a","b","c"])# 只生成2个元组- 返回的不是列表,是迭代器
省内存,只在遍历的时候才生成元组,不是一次性全部建好:
print(zip([1,2],[3,4]))# <zip object at 0x...>list(zip(...))# 把所有元组收集成列表- 传 N 个可迭代对象,元组长度就是 N
zip([1],[2],[3])# (1,2,3) 三元组3. 反向操作:解压 zip
*解包把一组元组还原回原始序列:
pairs=[(1,"x"),(2,"y")]nums,chars=zip(*pairs)# nums = (1,2)# chars = ('x','y')一句话总结
zip就是并行配对多个序列,逐个生成位置对应的元组,且是惰性迭代器。