说实话,以前我对上下文管理器的理解就停留在打开文件那一步。直到有一天,我花了整整一个下午啃完 Python 官方文档的相关章节,才意识到自己错过了什么。
那天我正写一个银行转账功能。A 账户扣钱,B 账户加钱。如果扣完钱后加钱失败,数据就乱了。我跟往常一样,拼命 try-except-finally,代码写得又长又臭。
同事路过看了一眼,摇摇头说:“你怎么不用事务上下文管理器?”
我这才发现,官方文档里清清楚楚写着:上下文管理器就是为这类场景准备的。它能把资源获取、异常回滚、资源释放这三件事打包成一个干净利落的代码块。
后来我改成了这样:
with transaction(db) as cursor:
cursor.execute(debit_query, (100, ‘A’))
cursor.execute(credit_query, (100, ‘B’))
只要事务管理器里抛出异常,两个操作自动回滚。代码量少了一半,逻辑清晰得像白水一样。
再比如访问数据库时,我以前总忘记关掉连接。用上下文管理器后,连接会在退出 with 块时自动归还给连接池,不会漏掉。官方文档里还提到一个细节:上下文管理器可以嵌套使用,每个管理器只关注自己那一层的资源。打开文件用 with open,操作数据库用 with db_transaction,多个资源就叠加起来。每个 with 块就像一个小盒子,各管各的事。
有次我写完代码,把嵌套的上下文管理器拆开看,发现每个块都只做一件事。一个负责锁定,一个负责修改,一个负责记录日志。改起来特别简单,要加逻辑就加个新的 with 块,不会动旧的代码。
官方文档还教你用 contextlib 里的 contextmanager 装饰器来定义自己的管理器。我按这个思路写了个“超时控制”上下文管理器,请求外部 API 时自动检查时间,超时就报错。代码就十几行,同事看了都想要。
回过头来想想,这些年我写了太多 try-finally 的脏代码。每次都要手动处理异常、手动释放资源,写十次错两次。而上下文管理器把模板代码藏起来了,只暴露业务逻辑。
上个月有个新人加入团队。他看我的代码问:“为什么这里没有 try-finally?”我笑着让他看上下文管理器的实现。他看完感叹:“原来事务逻辑可以这么处理。”那一刻我特别理解他的心情,因为我也有过同样的震撼。
编程的优雅不在花哨的技巧里,就藏在官方文档最朴素的提案里。上下文管理器不是新概念,但用好它,能让你的代码从“能跑”变成“好改”。它没有魔法,只是把该做的事做了,做得干净,做得彻底。下次你写代码觉得哪里不对劲时,不妨翻翻官方文档。说不定答案就安安静静躺在一行简单的方法定义里。
夜雨聆风