资讯详情

26.Python中的上下文管理器 (Context Manager)

《Python编程与道:Python视频课程语言先进 《Python编程与道:Python视频课程链接:https://edu.csdn.net/course/detail/28618

上下文管理器 (Context Manager)

管理资源:广泛应用于任何编程语言,如文件操作或数据库连接。 但这些资源是有限的。 因此,这些资源应在使用后释放。 如果不释放它们,则将导致资源泄漏,并可能导致系统变慢或崩溃。 如果用户有自动设置和释放资源的机制,这将非常有帮助。在Python它可以通过使用上下文管理器来实现,这有助于正确处理资源。

最常见的执行文件操作方法是使用with关键字如下:

# Python program showing  # a use of with keyword   with open("./files/test1.txt") as f:      data = f.read()  

以文件管理为例。 打开文件时,使用文件描述符(file descriptor),这是一种有限的资源。 一次只能打开一定数量的文件。 下面的程序演示它。

file_descriptors = []  for x in range(100000):      file_descriptors.append(open('./files/test1.txt', 'w'))  

一条错误的消息指出打开了太多的文件。 上面的例子是文件描述符的泄漏。 这是因为打开的文件太多,没有关闭。 程序员可能会忘记关闭打开的文件。

使用上下文管理器管理资源:

假设代码块引起异常,或者如果代码块具有复杂的算法,包含多个返回路径,则在所有位置关闭文件将变得非常麻烦。

使用文件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') 

这种情况下了将创建一个ContextManager对象。 这是在as关键字之后, 即manager,赋值给变量的。 在运行上述程序时,将依次执行以下操作:

  • __init__()
  • __enter__()
  • statement body (code inside the with block)
  • __exit__()[the parameters in this method are used to manage exceptions]

使用上下文管理器进行文件管理:

让我们应用以上概念创建一个有助于文件资源管理的类。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('./files/test1.txt', 'w') as f: 
    f.write('Test') 

print(f.closed) 
True

使用上下文管理器和with语句进行文件管理:

在执行with块时,将依次执行以下操作:

  • 当执行__init__方法时,将使用test1.txt作为文件名并使用w(write)作为模式创建FileManager对象。
  • __enter__方法以写模式(设置操作)打开test1.txt文件,并将FileManager对象返回到变量f。
  • 文字‘Test’ 写入了文件中。
  • __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}) 
    for element in data:
        print(element.name)
---------------------------------------------------------------------------

ServerSelectionTimeoutError               Traceback (most recent call last)

<ipython-input-4-99b2d8cb3efa> in <module>
     22     collection = mongo.connection.SampleDb.test
     23     data = collection.find({'_id': 1})
---> 24     for element in data:
     25         print(element.name)


C:\ProgramData\Anaconda3\lib\site-packages\pymongo\cursor.py in next(self)
   1154         if self.__empty:
   1155             raise StopIteration
-> 1156         if len(self.__data) or self._refresh():
   1157             if self.__manipulate:
   1158                 _db = self.__collection.database


C:\ProgramData\Anaconda3\lib\site-packages\pymongo\cursor.py in _refresh(self)
   1048 
   1049         if not self.__session:
-> 1050             self.__session = self.__collection.database.client._ensure_session()
   1051 
   1052         if self.__id is None:  # Query


C:\ProgramData\Anaconda3\lib\site-packages\pymongo\mongo_client.py in _ensure_session(self, session)
   1808             # Don't make implicit sessions causally consistent. Applications
   1809             # should always opt-in.
-> 1810             return self.__start_session(True, causal_consistency=False)
   1811         except (ConfigurationError, InvalidOperation):
   1812             # Sessions not supported, or multiple users authenticated.


C:\ProgramData\Anaconda3\lib\site-packages\pymongo\mongo_client.py in __start_session(self, implicit, **kwargs)
   1761 
   1762         # Raises ConfigurationError if sessions are not supported.
-> 1763         server_session = self._get_server_session()
   1764         opts = client_session.SessionOptions(**kwargs)
   1765         return client_session.ClientSession(


C:\ProgramData\Anaconda3\lib\site-packages\pymongo\mongo_client.py in _get_server_session(self)
   1794     def _get_server_session(self):
   1795         """Internal: start or resume a _ServerSession."""
-> 1796         return self._topology.get_server_session()
   1797 
   1798     def _return_server_session(self, server_session, lock):


C:\ProgramData\Anaconda3\lib\site-packages\pymongo\topology.py in get_server_session(self)
    483                             any_server_selector,
    484                             self._settings.server_selection_timeout,
--> 485                             None)
    486                 elif not self._description.readable_servers:
    487                     self._select_servers_loop(


C:\ProgramData\Anaconda3\lib\site-packages\pymongo\topology.py in _select_servers_loop(self, selector, timeout, address)
    207             if timeout == 0 or now > end_time:
    208                 raise ServerSelectionTimeoutError(
--> 209                     self._error_message(selector))
    210 
    211             self._ensure_opened()


ServerSelectionTimeoutError: localhost:27017: [WinError 10061] 由于目标计算机积极拒绝,无法连接。

使用上下文管理器和with语句进行数据库连接管理:

在执行with块时,将依次执行以下操作:

  • 当执行__init__方法时,将创建一个MongoDBConnectionManager对象,其中localhost为主机名,端口27017为端口。
  • __enter__方法打开mongodb连接,并将MongoDBConnectionManager对象返回到变量mongo。
  • 访问SampleDb数据库中的测试集合,并检索_id = 1的文档。 打印文档的名称字段。
  • __exit__方法负责在退出with块时关闭连接。

使用装饰器创建上下文管理器:

上下文管理器是如此有用,Python拥有专门用于它们的整个标准库模块contextlib,包含用于创建和使用上下文管理器的工具。 从类创建上下文管理器的一个不错的捷径是使用@contextmanager装饰器。 要使用它,装饰一个生成器函数,该函数只需调用一次yield即可。 调用yield之前的所有内容都被视为__enter__()的代码。 yield后面的所有内容都是__exit__()的代码。 让我们使用装饰器方法重写文件上下文管理器:

from contextlib import contextmanager

@contextmanager
def open_file(path, mode):
    the_file = open(path, mode)
    yield the_file
    the_file.close()

files = []

for x in range(100000):
    with open_file('foo.txt', 'w') as infile:
        files.append(infile)

for f in files:
    if not f.closed:
        print('not closed')

标签: 装饰连接器

锐单商城拥有海量元器件数据手册IC替代型号,打造 电子元器件IC百科大全!

锐单商城 - 一站式电子元器件采购平台