
写了这么多年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块外面不会报错。返回False或None,异常就继续向上抛。
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教程欢迎持续关注编程学习网。
扫码二维码 获取免费视频学习资料

- 本文固定链接: http://www.phpxs.com/post/14461/
- 转载请注明:转载必须在正文中标注并保留原文链接
- 扫码: 扫上方二维码获取免费视频资料