Python的上下文管理器,根据其定义,指实现上下文管理协议的方法的对象。主要用于安全的释放资源。核心是实现__enter__和__exit__两个方法,使用with关键字进行调用。 在工作中经常遇到资源的打开与释放,想到上下文管理器可以实现这种功能,特地做的学习和研究。
1.我们最常见的上下文管理是文件的打开关闭
如果不用上下文管理器,一个文件的打开和关闭可能是这样的
f = open( 'abc.txt' , 'w' ,encoding= 'utf-8' )
f.close()
为了确保关闭,我们可能会使用异常捕获语句
try:
f = open( 'abc.txt' , 'w' ,encoding= 'utf-8' )
f.write( 'xxx' )
finally:
f.close()
但这样的语句部分编译器会提示你f可能是未被定义的,比如Pycharm会标黄。因为前面f一旦出错,就没有定义成功f,何来关闭这一说法,f关闭的这个位置依然会出错。
此时我们可以使用上下文管理器
with open( 'abc.txt' , 'w' ,encoding= 'utf-8' ) as f:
f.write( 'xxx' )
2. 第二个常见的上下文是线程锁
使用线程锁来处理特定的文件时,可以确保文件不会被同时写入或读取,功能取决于你选择的锁类型。
import threading
my_lock = threading.Lock()
with my_lock:
print(123)
3. 数据库形式的上下文管理器
还记得mysql的连接吗,常见的流程如下,要先创建连接和游标,再执行语句后提交,最后关闭游标和连接。
import pymysql
conn = pymysql.connect(user='root', host='127.0.0.1', password='123456', port='3306', database='test_db')
cursor = conn.cursor()
cursor.execute('xxx')
conn.commit()
cursor.close()
conn.close()
此时我们可以将其改造成上下文管理器类。
class MySQLConnector:
def __init__(self):
self.user = 'root'
self.password = '123456'
self.host = '127.0.0.1'
self.database = 'test_db'
self.port = 3306
self.conn = None
self.cursor = None
def __enter__(self):
try:
self.conn = pymysql.connect(
user=self.user, password=self.password, host=self.host, database=self.database, port=self.port
)
self.cursor = self.conn.cursor()
except Exception as e:
traceback.print_exc()
raise e
def __exit__(self, exc_type, exc_val, exc_tb):
try:
self.conn.commit()
except Exception as e:
traceback.print_exc()
raise e
finally:
self.cursor.close()
self.conn.close()
使用的时候如下,其中cur就是我们在其中编写的enter方法的返回值,如果没有返回值,像上面的线程锁,就不需要as。
with MySQLConnector as cur:
cur.execute( 'xxx' )
上面的还可以做成带参数的类型,参数也可以用函数的**参数压缩一下成一个字典传进去。上下文管理器类中涉及的修改如下:
def init(self, config):
self.config = config
self.conn = pymysql.connect(**config)
使用时:
config = {
'user': 'root',
'password': '123456',
'host': '127.0.0.1',
'database': 'test_db',
'port': 3306
}
with MySQLConnector(config) as cur:
cur.execute('xxx')
这样就做成了一个通用的可以自己增加配置的类,还可以自行增加mysql的参数,比如字符集、游标类型这种。
4. 常用于服务器管理的连接类型的上下文管理器
paramiko是常用的服务器连接库,可以执行远程指令,上传下载文件等操作。主要用到的有SSHClient、Transport和SFTPClient三种方式。
这里对Transport方式作上下文管理器的实现
class MyTrans:
def __init__(self,config):
self.config = config
self.trans = None
def __enter__(self):
try:
self.trans = paramiko.Transport((self.config.get('host'),self.config.get('port')))
self.trans.connect(username=self.config.get('user'),password=self.config.get('password'))
return self.trans
except Exception as e:
traceback.print_exc()
raise e
def __exit__(self, exc_type, exc_val, exc_tb):
try:
self.trans.close()
except Exception as e:
traceback.print_exc()
raise e
同理可得到一个SFTP的上下文管理器
class MySFTP:
def __init__(self, trans):
self.trans = trans
self.sftp = None
def __enter__(self):
try:
self.sftp = paramiko.SFTPClient.from_transport(self.trans)
return self.sftp
except Exception as e:
traceback.print_exc()
raise e
def __exit__(self, exc_type, exc_val, exc_tb):
try:
self.sftp.close()
except Exception as e:
traceback.print_exc()
raise e
两者是联动使用的,使用方式如下:
with MyTrans(config) as trans:
with MySFTP(trans) as sftp:
sftp.put( 'xxx' , 'xxx' )
以上就是几种上下文管理器的常见用法,相信大家看完之后能有一定的提升!