📜  Python中的上下文管理器

📅  最后修改于: 2020-05-05 05:17:23             🧑  作者: Mango

管理资源:在任何编程语言中,诸如文件操作或数据库连接之类的资源使用都很普遍。但是这些资源供应有限。因此,主要问题在于确保使用后释放这些资源。如果不释放它们,则将导致资源泄漏,并可能导致系统变慢或崩溃。如果用户具有自动设置和拆卸资源的机制,这将非常有帮助。在Python中,可以通过使用上下文管理器来实现,这有助于对资源的正确处理。执行文件操作的最常见方式是使用with关键字,如下所示:

# Python程序显示与with关键字一起使用
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-finall y可以确保即使有异常也可以在使用后关闭文件资源。Python提供了一种简单的方法来管理资源:Context Managers:使用with关键字。上下文管理器可以使用类或函数(带有装饰器)来编写。

创建一个上下文管理器:
使用类创建上下文管理器时,用户需要确保该类具有以下方法:__ enter __()__exit __()。__enter __()返回需要管理的资源,而__exit __()不返回任何内容,而是执行清理操作。
首先,让我们创建一个名为ContextManager的简单类,以了解使用类创建上下文管理器的基本结构,如下所示:

# Python程序创建上下文管理器
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关键字(即manager)之后分配给变量的。在运行上述程序时,将依次执行以下操作:

  • __init__()
  • __enter__()
  • 语句主体(with块中的代码)
  • __exit __()[此方法中的参数用于管理异常]

使用上下文管理器进行文件管理:
让我们应用以上概念创建一个有助于文件资源管理的类。FileManager类有助于打开文件,写入/读取内容然后关闭它。

# Python程序使用上下文管理器显示文件管理
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()
# 加载文件
with FileManager('test.txt', 'w') as f:
    f.write('Test')
print(f.closed)

输出:

True

使用上下文管理器和with语句进行文件管理:
在执行with块时,将依次执行以下操作:

  • 一个FileManager的对象与创建的test.txt作为文件名和 w(写入)当在执行__init__方法的模式。
  • __enter__方法以写入模式(设置操作)打开test.txt文件,并将FileManager对象返回到变量f
  • 文本“ Test”被写入文件中。
  • __exit__方法负责在退出with块(拆卸操作)时关闭文件。
    打印(f.closed)运行时,输出为True,因为文件管理器已经关闭,否则需要显式地完成文件的照顾。

使用上下文管理器进行数据库连接管理:
让我们创建一个简单的数据库连接管理系统。一次可以打开的数据库连接的数量也受到限制(就像文件描述符一样)。因此,上下文管理器有助于管理与数据库的连接,因为程序员可能会忘记关闭连接。

# Python程序显示了用于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()
# 与本地主机连接
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块时关闭连接(拆卸操作)。