上下文管理器,英文context managers,在python官方文档中这样描述:
上下文管理器是一个对象,它定义了在执行 with 语句时要建立的运行时上下文。 上下文管理器处理进入和退出所需运行时上下文以执行代码块。 通常使用 with 语句(在 with 语句中描述),但是也可以通过直接调用它们的方法来使用。
知乎某大佬关于”上下文”的解释
f = open('a.txt', 'w')
f.write('22')
f.close()
f = open('a.txt', 'w')
try:
f.write('22')
except:
print('File eroor!')
finally:
f.close()
with open('b.txt', 'w') as f:
f.write('22')
可能会有这样疑问,不就是节省了一行文件关闭的代码而已嘛,也没看出有多高效啊!
这里先抛出结论:使用with管理上下文不仅可以在执行with语句体后自动执行退出操作(即__exit__方法定义语句),更重要的是能够在发生异常时,确保始终能执行退出操作、释放相应的资源。
在PEP343(该PEP文档由python之父龟叔发起,最早在python2.5版本引入。如想了解更多关于PEP知识,可阅读PEP入门指南),对上下文管理器给出如下定义:
上下文管理器是指提供了一对专门方法__enter __()和__exit __(),这些方法在with语句的主体进入和退出时被调用。在Python语言中添加了一个“ with”新语句,从而可以排除try / finally语句的标准用法。
也就是说,如果某个类定义了__enter__、__exit__方法,允许在语句体执行前进入该上下文、执行后退出该上下文,那么这样的类就可以称作是上下文管理器对象。而且该用法可用作对try/finally的替代,以处理可能存在的异常。
##_pyio.py
### Context manager ###
def __enter__(self): # That's a forward reference
"""Context management protocol. Returns self (an instance of IOBase)."""
self._checkClosed()
return self
def __exit__(self, *args):
"""Context management protocol. Calls close()"""
self.close()
注:open()函数返回一个FileIO类对象,FileIO类继承自RawIOBase类,RawIOBase又继承自IOBase类,而IOBase类定义了__enter__和__exit__方法,因而是一个上下文管理器对象。
"""IOBase also supports the :keyword:`with` statement. In this example,
fp is closed after the suite of the with statement is complete:
with open('spam.txt', 'r') as fp:
fp.write('Spam and eggs!')"""
from concurrent.futures import ThreadPoolExecutor, ProcessPoolExecutor
def multi_thread():
with ThreadPoolExecutor() as executor:
return list(executor.map(is_prime, PRIMES))
def multi_process():
with ProcessPoolExecutor() as executor:
return list(executor.map(is_prime, PRIMES))
##_base.py
def __enter__(self):
return self
def __exit__(self, exc_type, exc_val, exc_tb):
self.shutdown(wait=True)
return False
注:ThreadPoolExecutor和ProcessPoolExecutor两个类均继承自Executor类,而Excutor类是一个上下文管理器对象。需补充说明的是,正如上述示例代码给出的那样,__exit__()方法默认接收3个参数:exc_type、exc_val、exc_tb,如果with语句体发生异常,则3个参数分别赋值为type(error)、str(error)、error.__traceback__,否则均为None。
至此,我们可以得出这样的结论:一个上下文管理器对象是指定义了__enter__和__exit__方法的类对象;反之,定义了__enter__和__exit__方法的类可以应用with包装实现上下文管理器用法。
应用with包装上下文管理器时:执行with语句生成一个实例对象时,自动调用__enter__方法创建一个适用于上下文环境的类对象,完毕后无论是否触发异常都会调用__exit__方法执行退出操作。
import pymysql
class OpenMySQL(object):
def __init__(self, db):
self.conn = pymysql.connect(host="localhost", user="root", password="123456", db=db, charset='utf8')
def __enter__(self):
return self.conn.cursor()
def __exit__(self, exc_type, exc_val, exc_tb):
self.conn.commit()
self.conn.close()
if __name__ == '__main__':
with OpenMySQL('mytest') as sql:
sql_insert = 'insert into tbtest values (2);'
sql.execute(sql_insert)
代码功能执行正常,且相比于基本的数据库connect()–commit()–close()操作流程来说,要优雅许多。
注:写完例子后查阅源码发现pymysql.connect方法返回对象本身就是一个上下文管理器对象……。但仅仅用于举例的话,这也足以说明with包装上下文管理器的用法了。
本文对python中上下文管理器和with用法进行了简单介绍,包括:
-
上下文管理器是一个实现了__enter__、__exit__魔法方法的类对象
-
定义了__enter__、__exit__方法的类对象即可用作上下文管理器
- 上下文管理器通常使用with语句实现
-
with语句可实现简洁版的try/finally替代用法
评论前必须登录!
注册