编程学习网 > 编程语言 > Python > 写了这么多年Python,今天才知道with语句还能这么用!
2026
08-28

写了这么多年Python,今天才知道with语句还能这么用!


写了这么多年Python,今天才知道with语句还能这么用。以前我一直觉得with就是处理文件打开的,打开文件用with open,结束自动关闭,这已经够方便了。没想到它的本事远不止这些。

with语句真正厉害的地方是上下文管理器。任何实现了__enter____exit__方法的对象都能用在with后面。这就打开了一个新世界。

不信你看看这个。你写数据库查询的时候,是不是总得手动try...except...finally来关连接?

import sqlite3 

conn = sqlite3.connect('test.db')

try:

   cursor = conn.cursor()

   cursor.execute('SELECT  FROM users')

   for row in cursor:

       print(row)

except Exception as e:

   print(e)

finally:

   conn.close()

用了上下文管理器之后代码短了一半。

import sqlite3 

class DatabaseConnection:

   def __init__(self, db_name):

       self.db_name = db_name

   def __enter__(self):

       self.conn = sqlite3.connect(self.db_name)

       return self.conn

   def __exit__(self, exc_type, exc_val, exc_tb):

       self.conn.close()

with DatabaseConnection('test.db') as conn:

   cursor = conn.cursor()

   cursor.execute('SELECT  FROM users')

   for row in cursor:

       print(row)

这些代码都是你自己能写的。不用学什么深奥的理论,就实现两个方法:__enter__返回要操作的对象,__exit__做清理工作。Python会在with块结束时自动调用__exit__,即使里面出了异常也会调用。

还有更野的玩法。Python标准库里有个contextmanager装饰器,你连类都不用写。

from contextlib import contextmanager 

@contextmanager

def managed_resource(args, kwargs):

   resource = acquire_resource(args, kwargs)

   try:

       yield resource

   finally:

       release_resource(resource)

with managed_resource('some', 'params') as resource:

    使用资源

   pass

yield之前的是进入代码,yield之后的是退出代码。中间是你的业务逻辑。这种写法用在临时改环境变量、临时切换目录、计时器啥的特别好使。

说到计时器,你看看这个。

import time 

from contextlib import contextmanager

@contextmanager

def timer():

   start = time.time()

   try:

       yield

   finally:

       end = time.time()

       print(f'耗时: {end - start:.3f}')

with timer():

    要计时的代码

   for i in range(1000000):

       pass

再比如你想临时改一下工作目录搞个批量处理,完事了自动恢复。

import os 

from contextlib import contextmanager

@contextmanager

def change_dir(path):

   old_dir = os.getcwd()

   os.chdir(path)

   try:

       yield

   finally:

       os.chdir(old_dir)

with change_dir('/tmp'):

    在临时目录下干活

   pass

回到原来的目录

以前看到别人这么写觉得花里胡哨,后来项目里用了几个地方真香了。不用到处写try finally,代码干净,逻辑集中,读了也明白。

另外提醒一下,__exit__方法接收三个参数。你可以根据异常类型决定要不要吞掉异常。返回True就代表异常已处理,with块外面不会报错。返回FalseNone,异常就继续向上抛。

class IgnoreValueError: 

   def __enter__(self):

       return self

   def __exit__(self, exc_type, exc_val, exc_tb):

       if exc_type == ValueError:

           return True  

       return False

with IgnoreValueError():

   int('abc')   不会抛异常,被吞了

工作上写代码,不妨想想哪些地方做了获取资源再释放的事。文件、锁、数据库连接、线程池、定时器,都能用with封装一下。封装完,代码干净多了。

用了这么多年才发现with的妙处,说实话我有点后悔没早点研究。你要是平时写代码也遇到到处try finally的情况,赶紧试试。就加个类,或者一个装饰器,代码就变漂亮了。

以上就是“写了这么多年Python,今天才知道with语句还能这么用!的详细内容,想要了解更多Python教程欢迎持续关注编程学习网。  

扫码二维码 获取免费视频学习资料

Python编程学习

查 看2022高级编程视频教程免费获取