Python中的上下文管理器
管理资源:在任何编程语言中,文件操作或数据库连接等资源的使用都非常普遍。但这些资源供应有限。因此,主要问题在于确保在使用后释放这些资源。如果不释放它们,则会导致资源泄漏,并可能导致系统变慢或崩溃。如果用户有一种自动设置和拆除资源的机制,那将非常有帮助。在Python中,可以通过使用有助于正确处理资源的上下文管理器来实现。执行文件操作的最常见方法是使用with关键字,如下所示:
# Python program showing
# a use of with keyword
with open("test.txt") as f:
data = f.read()
让我们以文件管理为例。打开文件时,会消耗一个文件描述符,这是一个有限的资源。一个进程一次只能打开一定数量的文件。下面的程序演示了它。
file_descriptors = []
for x in range(100000):
file_descriptors.append(open('test.txt', 'w'))
输出:
Traceback (most recent call last):
File "context.py", line 3, in
OSError: [Errno 24] Too many open files: 'test.txt'
一条错误消息,指出打开的文件过多。上面的例子是一个文件描述符泄漏的例子。发生这种情况是因为打开的文件太多并且没有关闭。程序员可能会忘记关闭打开的文件。
使用上下文管理器管理资源:
假设一个代码块引发了一个异常,或者如果它有一个具有多个返回路径的复杂算法,那么在所有地方关闭一个文件变得很麻烦。
通常在其他语言中处理文件时try-except-finally用于确保文件资源在使用后关闭,即使出现异常也是如此。 Python提供了一种管理资源的简单方法:上下文管理器。使用with关键字。当它被评估时,它应该产生一个执行上下文管理的对象。上下文管理器可以使用类或函数(带有装饰器)来编写。
创建上下文管理器:
使用类创建上下文管理器时,用户需要确保该类具有方法: __enter__( )和__exit__() 。 __enter__() 返回需要管理的资源,而 __exit__() 不返回任何内容,而是执行清理操作。
首先,让我们创建一个简单的类ContextManager来了解使用类创建上下文管理器的基本结构,如下所示:
# Python program creating a
# context manager
class ContextManager():
def __init__(self):
print('init method called')
def __enter__(self):
print('enter method called')
return self
def __exit__(self, exc_type, exc_value, exc_traceback):
print('exit method called')
with ContextManager() as manager:
print('with statement block')
输出:
init method called
enter method called
with statement block
exit method called
在这种情况下,会创建一个 ContextManager 对象。这是在as关键字 ie manager之后分配给变量的。在运行上述程序时,将依次执行以下命令:
- __在里面__()
- __进入__()
- 语句体( with块内的代码)
- __exit__()[此方法中的参数用于管理异常]
使用上下文管理器进行文件管理:
让我们应用上述概念来创建一个有助于文件资源管理的类。 FileManager 类有助于打开文件、写入/读取内容然后关闭它。
# Python program showing
# file management using
# context manager
class FileManager():
def __init__(self, filename, mode):
self.filename = filename
self.mode = mode
self.file = None
def __enter__(self):
self.file = open(self.filename, self.mode)
return self.file
def __exit__(self, exc_type, exc_value, exc_traceback):
self.file.close()
# loading a file
with FileManager('test.txt', 'w') as f:
f.write('Test')
print(f.closed)
输出:
True
使用上下文管理器和 with 语句进行文件管理:
在执行with块时,依次发生以下操作:
- 执行 __init__ 方法时,使用test.txt作为文件名和w (写入)作为模式创建FileManager对象。
- __enter__ 方法以写入模式(设置操作)打开test.txt文件并将FileManager对象返回给变量f 。
- 文本“测试”被写入文件中。
- __exit__ 方法负责在退出with块(拆卸操作)时关闭文件。
运行print(f.closed)时,输出为True ,因为FileManager已经负责关闭文件,否则需要显式完成。
使用上下文管理器进行数据库连接管理:
让我们创建一个简单的数据库连接管理系统。一次可以打开的数据库连接数也是有限的(就像文件描述符一样)。因此,上下文管理器有助于管理与数据库的连接,因为程序员可能会忘记关闭连接。
# Python program shows the
# connection management
# for MongoDB
from pymongo import MongoClient
class MongoDBConnectionManager():
def __init__(self, hostname, port):
self.hostname = hostname
self.port = port
self.connection = None
def __enter__(self):
self.connection = MongoClient(self.hostname, self.port)
return self
def __exit__(self, exc_type, exc_value, exc_traceback):
self.connection.close()
# connecting with a localhost
with MongoDBConnectionManager('localhost', '27017') as mongo:
collection = mongo.connection.SampleDb.test
data = collection.find({'_id': 1})
print(data.get('name'))
使用上下文管理器和 with 语句进行数据库连接管理:
在执行with块时,依次发生以下操作:
- 执行 __init__ 方法时,会创建一个MongoDBConnectionManager对象,其中localhost作为主机名, 27017作为端口。
- __enter__ 方法打开 mongodb 连接并将MongoDBConnectionManager对象返回给变量mongo 。
- 访问 SampleDb 数据库中的测试集合并检索_id =1 的文档。打印文档的名称字段。
- __exit__ 方法负责在退出with块(拆卸操作)时关闭连接。