contextlib
— Utilities for with
-statement contextswith
语句上下文的实用程序¶
with
-statement contextsSource code: Lib/contextlib.py
This module provides utilities for common tasks involving the 此模块为涉及with
statement. with
语句的常见任务提供实用程序。For more information see also Context Manager Types and With Statement Context Managers.有关更多信息,请参阅上下文管理器类型和With语句上下文管理器。
Utilities公用事业¶
Functions and classes provided:提供的功能和类:
-
class
contextlib.
AbstractContextManager
¶ An abstract base class for classes that implement实现object.__enter__()
andobject.__exit__()
.object.__enter__()
和object.__exit__()
的类的抽象基类__输入__()
。A default implementation for提供了object.__enter__()
is provided which returnsself
whileobject.__exit__()
is an abstract method which by default returnsNone
. See also the definition of Context Manager Types.object.__enter__()
的默认实现enter__()
,它返回self
同时对象object.__exit__()
是一个抽象方法,默认情况下返回None
。另请参见上下文管理器类型的定义。New in version 3.6.版本3.6中新增。
-
class
contextlib.
AbstractAsyncContextManager
¶ An abstract base class for classes that implement实现object.__aenter__()
andobject.__aexit__()
.object.__aenter__()
和object.__aexit__()
的类的抽象基类。A default implementation for提供了object.__aenter__()
is provided which returnsself
whileobject.__aexit__()
is an abstract method which by default returnsNone
.object.__aenter__()
的默认实现提供了aenter__()
,它返回self
,同时object.__aexit__()
是一个抽象方法,默认情况下返回None
。See also the definition of Asynchronous Context Managers.另请参见异步上下文管理器的定义。New in version 3.7.版本3.7中新增。
-
@
contextlib.
contextmanager
¶ This function is a decorator that can be used to define a factory function for此函数是一个装饰器,可用于定义with
statement context managers, without needing to create a class or separate__enter__()
and__exit__()
methods.with
语句上下文管理器的工厂函数,而无需创建类或单独的__enter__()
和__exit__()
方法。While many objects natively support use in with statements, sometimes a resource needs to be managed that isn’t a context manager in its own right, and doesn’t implement a虽然许多对象本机支持在语句中使用,但有时需要管理的资源本身不是上下文管理器,并且没有实现用于close()
method for use withcontextlib.closing
contextlib.closing
的close()
方法An abstract example would be the following to ensure correct resource management:以下是一个抽象的例子,以确保正确的资源管理:from contextlib import contextmanager
@contextmanager
def managed_resource(*args, **kwds):
# Code to acquire resource, e.g.:
resource = acquire_resource(*args, **kwds)
try:
yield resource
finally:
# Code to release resource, e.g.:
release_resource(resource)
>>> with managed_resource(timeout=3600) as resource:
... # Resource is released at the end of this block,
... # even if code in the block raises an exceptionThe function being decorated must return a generator-iterator when called.被修饰的函数在被调用时必须返回生成器迭代器。This iterator must yield exactly one value, which will be bound to the targets in the此迭代器必须只产生一个值,该值将绑定到with
statement’sas
clause, if any.with
语句的as
子句中的目标(如果有的话)。At the point where the generator yields, the block nested in the在生成器生成时,执行嵌套在with
statement is executed.with
语句中的块。The generator is then resumed after the block is exited.然后,在块退出后恢复生成器。If an unhandled exception occurs in the block, it is reraised inside the generator at the point where the yield occurred.如果块中发生未经处理的异常,则会在生成程序内部发生屈服点时重新引发该异常。Thus, you can use a因此,您可以使用try
…except
…finally
statement to trap the error (if any), or ensure that some cleanup takes place.try
…except
…finally
语句来捕获错误(如果有的话),或者确保进行一些清理。If an exception is trapped merely in order to log it or to perform some action (rather than to suppress it entirely), the generator must reraise that exception.如果捕获异常只是为了记录它或执行某些操作(而不是完全抑制它),则生成器必须重新评估该异常。Otherwise the generator context manager will indicate to the否则,生成器上下文管理器将向with
statement that the exception has been handled, and execution will resume with the statement immediately following thewith
statement.with
语句指示异常已被处理,并在with
语句之后立即使用该语句恢复执行。contextmanager()
usesContextDecorator
so the context managers it creates can be used as decorators as well as inwith
statements.contextmanager()
使用ContextDecorator
,因此它创建的上下文管理器既可以用作装饰器,也可以用于with
语句。When used as a decorator, a new generator instance is implicitly created on each function call (this allows the otherwise “one-shot” context managers created by当用作装饰器时,会在每个函数调用上隐式创建一个新的生成器实例(这允许contextmanager()
to meet the requirement that context managers support multiple invocations in order to be used as decorators).contextmanager()
创建的其他“一次性”上下文管理器满足上下文管理器支持多个调用才能用作装饰器的要求)。Changed in version 3.2:版本3.2中更改:Use ofContextDecorator
.ContextDecorator
的使用。
-
@
contextlib.
asynccontextmanager
¶ Similar to类似于contextmanager()
, but creates an asynchronous context manager.contextmanager()
,但创建了一个异步上下文管理器。This function is a decorator that can be used to define a factory function for此函数是一个装饰器,可用于定义async with
statement asynchronous context managers, without needing to create a class or separate__aenter__()
and__aexit__()
methods.async with
语句异步上下文管理器的工厂函数,而无需创建类或单独的__aenter__()
和__aexit_()
方法。It must be applied to an asynchronous generator function.它必须应用于异步生成器函数。A simple example:一个简单的例子:from contextlib import asynccontextmanager
@asynccontextmanager
async def get_connection():
conn = await acquire_db_connection()
try:
yield conn
finally:
await release_db_connection(conn)
async def get_all_users():
async with get_connection() as conn:
return conn.query('SELECT ...')New in version 3.7.版本3.7中新增。Context managers defined with用asynccontextmanager()
can be used either as decorators or withasync with
statements:asynccontextmanager()
定义的上下文管理器既可以用作装饰器,也可以与async with
语句一起使用:import time
from contextlib import asynccontextmanager
@asynccontextmanager
async def timeit():
now = time.monotonic()
try:
yield
finally:
print(f'it took {time.monotonic() - now}s to run')
@timeit()
async def main():
# ... async code ...When used as a decorator, a new generator instance is implicitly created on each function call.当用作装饰器时,会在每个函数调用上隐式创建一个新的生成器实例。This allows the otherwise “one-shot” context managers created by这允许asynccontextmanager()
to meet the requirement that context managers support multiple invocations in order to be used as decorators.asynccontextmanager()
创建的其他“一次性”上下文管理器满足上下文管理器支持多个调用以便用作装饰器的要求。Changed in version 3.10:版本3.10中更改:Async context managers created with使用asynccontextmanager()
can be used as decorators.asynccontextmanager()
创建的异步上下文管理器可以用作装饰器。
-
contextlib.
closing
(thing)¶ Return a context manager that closes thing upon completion of the block. This is basically equivalent to:返回一个上下文管理器,该管理器在块完成时关闭对象。这基本上相当于:from contextlib import contextmanager
@contextmanager
def closing(thing):
try:
yield thing
finally:
thing.close()And lets you write code like this:让您编写这样的代码:from contextlib import closing
from urllib.request import urlopen
with closing(urlopen('https://www.python.org')) as page:
for line in page:
print(line)without needing to explicitly close而无需显式关闭page
.page
。Even if an error occurs,即使出现错误,在退出page.close()
will be called when thewith
block is exited.with
块时也会调用page.close()
。
-
class
contextlib.
aclosing
(thing)¶ Return an async context manager that calls the返回一个异步上下文管理器,该管理器在块完成时调用事物的aclose()
method of thing upon completion of the block.aclose()
方法。This is basically equivalent to:这基本上相当于:from contextlib import asynccontextmanager
@asynccontextmanager
async def aclosing(thing):
try:
yield thing
finally:
await thing.aclose()Significantly,值得注意的是,当异步生成器由于aclosing()
supports deterministic cleanup of async generators when they happen to exit early bybreak
or an exception. For example:break
或异常而提前退出时,aclosing()
支持异步生成器的确定性清理。例如:from contextlib import aclosing
async with aclosing(my_generator()) as values:
async for value in values:
if value == 42:
breakThis pattern ensures that the generator’s async exit code is executed in the same context as its iterations (so that exceptions and context variables work as expected, and the exit code isn’t run after the lifetime of some task it depends on).此模式确保生成器的异步退出代码在与其迭代相同的上下文中执行(以便异常和上下文变量按预期工作,并且退出代码不会在它所依赖的某个任务的生存期之后运行)。New in version 3.10.版本3.10中新增。
-
contextlib.
nullcontext
(enter_result=None)¶ Return a context manager that returns enter_result from返回一个上下文管理器,该管理器从__enter__
, but otherwise does nothing.__enter__
返回enter_result,但在其他情况下不执行任何操作。It is intended to be used as a stand-in for an optional context manager, for example:它旨在用作可选上下文管理器的替身,例如:def myfunction(arg, ignore_exceptions=False):
if ignore_exceptions:
# Use suppress to ignore all exceptions.
cm = contextlib.suppress(Exception)
else:
# Do not ignore any exceptions, cm has no effect.
cm = contextlib.nullcontext()
with cm:
# Do somethingAn example using enter_result:使用enter_result的示例:def process_file(file_or_path):
if isinstance(file_or_path, str):
# If string, open file
cm = open(file_or_path)
else:
# Caller is responsible for closing file
cm = nullcontext(file_or_path)
with cm as file:
# Perform processing on the fileIt can also be used as a stand-in for asynchronous context managers:它还可以用作异步上下文管理器的替身:async def send_http(session=None):
if not session:
# If no http session, create it with aiohttp
cm = aiohttp.ClientSession()
else:
# Caller is responsible for closing the session
cm = nullcontext(session)
async with cm as session:
# Send http requests with sessionNew in version 3.7.版本3.7中新增。Changed in version 3.10:版本3.10中更改:asynchronous context manager support was added.添加了异步上下文管理器支持。
-
contextlib.
suppress
(*exceptions)¶ Return a context manager that suppresses any of the specified exceptions if they occur in the body of a返回一个上下文管理器,如果指定的异常出现在with
statement and then resumes execution with the first statement following the end of thewith
statement.with
语句的正文中,则该上下文管理器将抑制这些异常,然后使用with
语句结束后的第一个语句继续执行。As with any other mechanism that completely suppresses exceptions, this context manager should be used only to cover very specific errors where silently continuing with program execution is known to be the right thing to do.与任何其他完全抑制异常的机制一样,此上下文管理器应仅用于覆盖非常特定的错误,在这些错误中,静默地继续执行程序是正确的做法。For example:
from contextlib import suppress
with suppress(FileNotFoundError):
os.remove('somefile.tmp')
with suppress(FileNotFoundError):
os.remove('someotherfile.tmp')This code is equivalent to:此代码等效于:try:
os.remove('somefile.tmp')
except FileNotFoundError:
pass
try:
os.remove('someotherfile.tmp')
except FileNotFoundError:
passThis context manager is reentrant.此上下文管理器是可重入的。New in version 3.4.版本3.4中新增。
-
contextlib.
redirect_stdout
(new_target)¶ Context manager for temporarily redirecting上下文管理器,用于将sys.stdout
to another file or file-like object.sys.stdout
临时重定向到另一个文件或类似文件的对象。This tool adds flexibility to existing functions or classes whose output is hardwired to stdout.该工具为其输出硬连接到stdout的现有函数或类增加了灵活性。For example, the output of例如,help()
normally is sent to sys.stdout.help()
的输出通常发送到sys.stdout。You can capture that output in a string by redirecting the output to an通过将输出重定向到io.StringIO
object.io.StringIO
对象,可以在字符串中捕获该输出。The replacement stream is returned from the替换流是从__enter__
method and so is available as the target of thewith
statement:__enter__
方法返回的,因此可用作with
语句的目标:with redirect_stdout(io.StringIO()) as f:
help(pow)
s = f.getvalue()To send the output of要将help()
to a file on disk, redirect the output to a regular file:help()
的输出发送到磁盘上的文件,请将输出重定向到常规文件:with open('help.txt', 'w') as f:
with redirect_stdout(f):
help(pow)To send the output of要将help()
to sys.stderr:help()
的输出发送到sys.stderr,请执行以下操作:with redirect_stdout(sys.stderr):
help(pow)Note that the global side effect on请注意,sys.stdout
means that this context manager is not suitable for use in library code and most threaded applications.sys.stdout
的全局副作用意味着此上下文管理器不适合在库代码和大多数线程应用程序中使用。It also has no effect on the output of subprocesses.它也对子流程的输出没有影响。However, it is still a useful approach for many utility scripts.然而,对于许多实用程序脚本来说,它仍然是一种有用的方法。This context manager is reentrant.此上下文管理器是可重入的。New in version 3.4.版本3.4中新增。
-
contextlib.
redirect_stderr
(new_target)¶ Similar to类似于redirect_stdout()
but redirectingsys.stderr
to another file or file-like object.redirect_stdout()
,但将sys.stderr
重定向到另一个文件或类似文件的对象。This context manager is reentrant.此上下文管理器是可重入的。New in version 3.5.版本3.5中新增。
-
class
contextlib.
ContextDecorator
¶ A base class that enables a context manager to also be used as a decorator.一个基类,它使上下文管理器也可以用作装饰器。Context managers inheriting from从ContextDecorator
have to implement__enter__
and__exit__
as normal.ContextDecorator
继承的上下文管理器必须像往常一样实现__enter__
和__exit__
。__exit__
retains its optional exception handling even when used as a decorator.即使用作装饰器,也保留其可选的异常处理。ContextDecorator
is used bycontextmanager()
, so you get this functionality automatically.ContextDecorator
由contextmanager()
使用,因此您可以自动获得此功能。Example ofContextDecorator
:ContextDecorator
示例:from contextlib import ContextDecorator
class mycontext(ContextDecorator):
def __enter__(self):
print('Starting')
return self
def __exit__(self, *exc):
print('Finishing')
return False
>>> @mycontext()
... def function():
... print('The bit in the middle')
...
>>> function()
Starting
The bit in the middle
Finishing
>>> with mycontext():
... print('The bit in the middle')
...
Starting
The bit in the middle
FinishingThis change is just syntactic sugar for any construct of the following form:这种变化只是以下形式的任何结构的句法糖:def f():
with cm():
# Do stuffContextDecorator
lets you instead write:让您改为编写:@cm()
def f():
# Do stuffIt makes it clear that the它清楚地表明,cm适用于整个函数,而不仅仅是其中的一部分(保存缩进级别也很好)。cm
applies to the whole function, rather than just a piece of it (and saving an indentation level is nice, too).Existing context managers that already have a base class can be extended by using已经有基类的现有上下文管理器可以通过使用ContextDecorator
as a mixin class:ContextDecorator
作为mixin类进行扩展:from contextlib import ContextDecorator
class mycontext(ContextBaseClass, ContextDecorator):
def __enter__(self):
return self
def __exit__(self, *exc):
return FalseNote
As the decorated function must be able to be called multiple times, the underlying context manager must support use in multiple由于修饰函数必须能够被多次调用,因此底层上下文管理器必须支持在多个with
statements.with
语句中使用。If this is not the case, then the original construct with the explicit如果不是这种情况,那么应该使用函数内部带有显式with
statement inside the function should be used.with
语句的原始构造。New in version 3.2.版本3.2中新增。
-
class
contextlib.
AsyncContextDecorator
¶ Similar to类似于ContextDecorator
but only for asynchronous functions.ContextDecorator
,但仅适用于异步函数。Example ofAsyncContextDecorator
:AsyncContextDecorator
示例:from asyncio import run
from contextlib import AsyncContextDecorator
class mycontext(AsyncContextDecorator):
async def __aenter__(self):
print('Starting')
return self
async def __aexit__(self, *exc):
print('Finishing')
return False
>>> @mycontext()
... async def function():
... print('The bit in the middle')
...
>>> run(function())
Starting
The bit in the middle
Finishing
>>> async def function():
... async with mycontext():
... print('The bit in the middle')
...
>>> run(function())
Starting
The bit in the middle
FinishingNew in version 3.10.版本3.10中新增。
-
class
contextlib.
ExitStack
¶ A context manager that is designed to make it easy to programmatically combine other context managers and cleanup functions, especially those that are optional or otherwise driven by input data.一种上下文管理器,其设计目的是使其他上下文管理器和清理函数(尤其是那些可选的或由输入数据驱动的函数)能够以编程方式轻松组合。For example, a set of files may easily be handled in a single with statement as follows:例如,一组文件可以很容易地在单个with
语句中处理,如下所示:with ExitStack() as stack:
files = [stack.enter_context(open(fname)) for fname in filenames]
# All opened files will automatically be closed at the end of
# the with statement, even if attempts to open files later
# in the list raise an exceptionThe__enter__()
method returns theExitStack
instance, and performs no additional operations.__enter__()
方法返回ExitStack
实例,不执行其他操作。Each instance maintains a stack of registered callbacks that are called in reverse order when the instance is closed (either explicitly or implicitly at the end of a每个实例都维护一个已注册回调堆栈,当实例关闭时(在with
statement).with
语句末尾显式或隐式),这些回调将以相反的顺序调用。Note that callbacks are not invoked implicitly when the context stack instance is garbage collected.请注意,当上下文堆栈实例被垃圾回收时,回调不会被隐式调用。This stack model is used so that context managers that acquire their resources in their使用此堆栈模型可以正确处理在__init__
method (such as file objects) can be handled correctly.__init__
方法中获取资源的上下文管理器(如文件对象)。Since registered callbacks are invoked in the reverse order of registration, this ends up behaving as if multiple nested由于已注册的回调是以与注册相反的顺序调用的,因此最终的行为就像已注册的一组回调使用了多个嵌套的with
statements had been used with the registered set of callbacks.with
语句一样。This even extends to exception handling - if an inner callback suppresses or replaces an exception, then outer callbacks will be passed arguments based on that updated state.这甚至扩展到异常处理——如果内部回调抑制或替换异常,那么外部回调将根据更新后的状态传递参数。This is a relatively low level API that takes care of the details of correctly unwinding the stack of exit callbacks. It provides a suitable foundation for higher level context managers that manipulate the exit stack in application specific ways.这是一个相对较低级别的API,负责正确展开退出回调堆栈的细节。它为以特定于应用程序的方式操作出口堆栈的更高级别上下文管理器提供了合适的基础。New in version 3.3.版本3.3中新增。-
enter_context
(cm)¶ Enters a new context manager and adds its进入一个新的上下文管理器,并将其__exit__()
method to the callback stack. The return value is the result of the context manager’s own__enter__()
method.__exit_()
方法添加到回调堆栈中。返回值是上下文管理器自己的__enter__()
方法的结果。These context managers may suppress exceptions just as they normally would if used directly as part of a如果直接作为with
statement.with
语句的一部分使用,这些上下文管理器可能会抑制异常,就像通常情况下一样。
-
push
(exit)¶ Adds a context manager’s将上下文管理器的__exit__()
method to the callback stack.__exit_()
方法添加到回调堆栈中。As由于未调用__enter__
is not invoked, this method can be used to cover part of an__enter__()
implementation with a context manager’s own__exit__()
method.__enter__
,此方法可用于用上下文管理器自己的__exit__()
方法覆盖__enter__()
实现的一部分。If passed an object that is not a context manager, this method assumes it is a callback with the same signature as a context manager’s如果传递的对象不是上下文管理器,则此方法假定它是一个回调,具有与上下文管理器的__exit__()
method and adds it directly to the callback stack.__exit_()
方法相同的签名,并将其直接添加到回调堆栈中。By returning true values, these callbacks can suppress exceptions the same way context manager通过返回真值,这些回调可以像上下文管理器__exit__()
methods can.__exit_()
方法一样抑制异常。The passed in object is returned from the function, allowing this method to be used as a function decorator.传入的对象是从函数返回的,允许将此方法用作函数装饰器。
-
callback
(callback, /, *args, **kwds)¶ Accepts an arbitrary callback function and arguments and adds it to the callback stack.接受任意回调函数和参数,并将其添加到回调堆栈中。Unlike the other methods, callbacks added this way cannot suppress exceptions (as they are never passed the exception details).与其他方法不同,以这种方式添加的回调无法抑制异常(因为它们从未传递异常详细信息)。The passed in callback is returned from the function, allowing this method to be used as a function decorator.传入的回调是从函数返回的,允许将此方法用作函数装饰器。
-
pop_all
()¶ Transfers the callback stack to a fresh将回调堆栈传输到新的ExitStack
instance and returns it.ExitStack
实例并返回它。No callbacks are invoked by this operation - instead, they will now be invoked when the new stack is closed (either explicitly or implicitly at the end of a此操作不会调用回调,而是在关闭新堆栈时(在with
statement).with
语句末尾显式或隐式)调用它们。For example, a group of files can be opened as an “all or nothing” operation as follows:例如,一组文件可以作为“要么全部打开,要么什么都不打开”操作打开,如下所示:with ExitStack() as stack:
files = [stack.enter_context(open(fname)) for fname in filenames]
# Hold onto the close method, but don't call it yet.
close_files = stack.pop_all().close
# If opening any file fails, all previously opened files will be
# closed automatically. If all files are opened successfully,
# they will remain open even after the with statement ends.
# close_files() can then be invoked explicitly to close them all.
-
close
()¶ Immediately unwinds the callback stack, invoking callbacks in the reverse order of registration.立即展开回调堆栈,以与注册相反的顺序调用回调。For any context managers and exit callbacks registered, the arguments passed in will indicate that no exception occurred.对于任何已注册的上下文管理器和退出回调,传入的参数将指示没有发生异常。
-
-
class
contextlib.
AsyncExitStack
¶ An asynchronous context manager, similar to一个异步上下文管理器,类似于ExitStack
, that supports combining both synchronous and asynchronous context managers, as well as having coroutines for cleanup logic.ExitStack
,支持组合同步和异步上下文管理程序,并具有用于清理逻辑的协同程序。The未实现close()
method is not implemented,aclose()
must be used instead.close()
方法,必须改用aclose()
。-
coroutine
enter_async_context
(cm)¶ Similar to类似于enter_context()
but expects an asynchronous context manager.enter_context()
,但需要一个异步上下文管理器。
-
push_async_exit
(exit)¶ Similar to类似于push()
but expects either an asynchronous context manager or a coroutine function.push()
,但需要异步上下文管理器或协程函数。
-
push_async_callback
(callback, /, *args, **kwds)¶ Similar to类似于callback()
but expects a coroutine function.callback()
,但需要一个协程函数。
-
coroutine
aclose
()¶ Similar to类似于close()
but properly handles awaitables.close()
,但可以正确处理可用的内容。
Continuing the example for继续asynccontextmanager()
:asynccontextmanager()
的示例:async with AsyncExitStack() as stack:
connections = [await stack.enter_async_context(get_connection())
for i in range(5)]
# All opened connections will automatically be released at the end of
# the async with statement, even if attempts to open a connection
# later in the list raise an exception.New in version 3.7.版本3.7中新增。-
coroutine
Examples and Recipes示例和食谱¶
This section describes some examples and recipes for making effective use of the tools provided by 本节介绍了一些有效使用contextlib
.contextlib
提供的工具的示例和方法。
Supporting a variable number of context managers支持可变数量的上下文管理器¶
The primary use case for ExitStack
is the one given in the class documentation: supporting a variable number of context managers and other cleanup operations in a single with
statement. ExitStack
的主要用例是类文档中给出的:在单个with
语句中支持可变数量的上下文管理器和其他清理操作。The variability may come from the number of context managers needed being driven by user input (such as opening a user specified collection of files), or from some of the context managers being optional:可变性可能来自于由用户输入(例如打开用户指定的文件集合)驱动所需的上下文管理器的数量,或者来自于一些可选的上下文管理程序:
with ExitStack() as stack:
for resource in resources:
stack.enter_context(resource)
if need_special_resource():
special = acquire_special_resource()
stack.callback(release_special_resource, special)
# Perform operations that use the acquired resources
As shown, 如图所示,ExitStack
also makes it quite easy to use with
statements to manage arbitrary resources that don’t natively support the context management protocol.ExitStack
还可以很容易地与with
语句一起使用,以管理本机不支持上下文管理协议的任意资源。
Catching exceptions from __enter__
methods从__enter__
方法捕获异常¶
__enter__
methodsIt is occasionally desirable to catch exceptions from an 偶尔需要从__enter__
method implementation, without inadvertently catching exceptions from the with
statement body or the context manager’s __exit__
method. __enter__
方法实现捕获异常,而不会无意中从with
语句体或上下文管理器的__exit__
方法捕获异常。By using 通过使用ExitStack
the steps in the context management protocol can be separated slightly in order to allow this:ExitStack
,上下文管理协议中的步骤可以稍微分开,以便实现以下目的:
stack = ExitStack()
try:
x = stack.enter_context(cm)
except Exception:
# handle __enter__ exception
else:
with stack:
# Handle normal case
Actually needing to do this is likely to indicate that the underlying API should be providing a direct resource management interface for use with 实际上需要这样做可能表明底层API应该提供一个直接的资源管理接口,用于try
/except
/finally
statements, but not all APIs are well designed in that regard. try
/except
/finally
语句,但并不是所有的API都在这方面设计得很好。When a context manager is the only resource management API provided, then 当上下文管理器是提供的唯一资源管理API时,ExitStack
can make it easier to handle various situations that can’t be handled directly in a with
statement.ExitStack
可以更容易地处理不能在with
语句中直接处理的各种情况。
Cleaning up in an __enter__
implementation在__enter__
实现中进行清理¶
__enter__
implementationAs noted in the documentation of 如ExitStack.push()
, this method can be useful in cleaning up an already allocated resource if later steps in the __enter__()
implementation fail.ExitStack.push()
的文档中所述,如果__enter__()
实现中的后续步骤失败,此方法在清理已分配的资源时非常有用。
Here’s an example of doing this for a context manager that accepts resource acquisition and release functions, along with an optional validation function, and maps them to the context management protocol:以下是一个为上下文管理器执行此操作的示例,该上下文管理器接受资源获取和释放功能以及可选的验证功能,并将它们映射到上下文管理协议:
from contextlib import contextmanager, AbstractContextManager, ExitStack
class ResourceManager(AbstractContextManager):
def __init__(self, acquire_resource, release_resource, check_resource_ok=None):
self.acquire_resource = acquire_resource
self.release_resource = release_resource
if check_resource_ok is None:
def check_resource_ok(resource):
return True
self.check_resource_ok = check_resource_ok
@contextmanager
def _cleanup_on_error(self):
with ExitStack() as stack:
stack.push(self)
yield
# The validation check passed and didn't raise an exception
# Accordingly, we want to keep the resource, and pass it
# back to our caller
stack.pop_all()
def __enter__(self):
resource = self.acquire_resource()
with self._cleanup_on_error():
if not self.check_resource_ok(resource):
msg = "Failed validation for {!r}"
raise RuntimeError(msg.format(resource))
return resource
def __exit__(self, *exc_details):
# We don't need to duplicate any of our resource release logic
self.release_resource()
Replacing any use of try-finally
and flag variables替换try-finally
和标记变量的任何使用¶
try-finally
and flag variablesA pattern you will sometimes see is a 您有时会看到一种模式,即带有标志变量的try-finally
statement with a flag variable to indicate whether or not the body of the finally
clause should be executed. try-finally
语句,用于指示是否应执行finally
子句的主体。In its simplest form (that can’t already be handled just by using an 在最简单的形式中(不能仅通过使用except
clause instead), it looks something like this:except
子句来处理),它看起来是这样的:
cleanup_needed = True
try:
result = perform_operation()
if result:
cleanup_needed = False
finally:
if cleanup_needed:
cleanup_resources()
As with any 与任何基于try
statement based code, this can cause problems for development and review, because the setup code and the cleanup code can end up being separated by arbitrarily long sections of code.try
语句的代码一样,这可能会给开发和审查带来问题,因为设置代码和清理代码最终可能会被任意长的代码段分隔开。
ExitStack
makes it possible to instead register a callback for execution at the end of a 使得可以在with
statement, and then later decide to skip executing that callback:with
语句结束时注册一个回调以供执行,然后决定跳过执行该回调:
from contextlib import ExitStack
with ExitStack() as stack:
stack.callback(cleanup_resources)
result = perform_operation()
if result:
stack.pop_all()
This allows the intended cleanup up behaviour to be made explicit up front, rather than requiring a separate flag variable.这允许预先明确预期的清理行为,而不需要单独的标志变量。
If a particular application uses this pattern a lot, it can be simplified even further by means of a small helper class:如果一个特定的应用程序经常使用这种模式,则可以通过一个小的辅助类来进一步简化它:
from contextlib import ExitStack
class Callback(ExitStack):
def __init__(self, callback, /, *args, **kwds):
super().__init__()
self.callback(callback, *args, **kwds)
def cancel(self):
self.pop_all()
with Callback(cleanup_resources) as cb:
result = perform_operation()
if result:
cb.cancel()
If the resource cleanup isn’t already neatly bundled into a standalone function, then it is still possible to use the decorator form of 如果资源清理还没有整齐地绑定到一个独立的函数中,那么仍然可以使用ExitStack.callback()
to declare the resource cleanup in advance:ExitStack.callback()
的装饰器形式提前声明资源清理:
from contextlib import ExitStack
with ExitStack() as stack:
@stack.callback
def cleanup_resources():
...
result = perform_operation()
if result:
stack.pop_all()
Due to the way the decorator protocol works, a callback function declared this way cannot take any parameters. 由于decorator协议的工作方式,以这种方式声明的回调函数不能接受任何参数。Instead, any resources to be released must be accessed as closure variables.相反,任何要释放的资源都必须作为闭包变量进行访问。
Using a context manager as a function decorator使用上下文管理器作为函数装饰器¶
ContextDecorator
makes it possible to use a context manager in both an ordinary 使得可以在普通的with
statement and also as a function decorator.with
语句中使用上下文管理器,也可以将其用作函数装饰器。
For example, it is sometimes useful to wrap functions or groups of statements with a logger that can track the time of entry and time of exit. 例如,有时使用记录器包装函数或语句组是有用的,该记录器可以跟踪进入时间和退出时间。Rather than writing both a function decorator and a context manager for the task, inheriting from 从ContextDecorator
provides both capabilities in a single definition:ContextDecorator
继承不是为任务同时编写函数装饰器和上下文管理器,而是在一个定义中提供这两种功能:
from contextlib import ContextDecorator
import logging
logging.basicConfig(level=logging.INFO)
class track_entry_and_exit(ContextDecorator):
def __init__(self, name):
self.name = name
def __enter__(self):
logging.info('Entering: %s', self.name)
def __exit__(self, exc_type, exc, exc_tb):
logging.info('Exiting: %s', self.name)
Instances of this class can be used as both a context manager:此类的实例既可以用作上下文管理器:
with track_entry_and_exit('widget loader'):
print('Some time consuming activity goes here')
load_widget()
And also as a function decorator:同时作为函数装饰器:
@track_entry_and_exit('widget loader')
def activity():
print('Some time consuming activity goes here')
load_widget()
Note that there is one additional limitation when using context managers as function decorators: there’s no way to access the return value of 请注意,使用上下文管理器作为函数装饰器时还有一个额外的限制:无法访问__enter__()
. __enter__()
的返回值。If that value is needed, then it is still necessary to use an explicit 如果需要该值,那么仍然需要使用显式with
statement.with
语句。
Single use, reusable and reentrant context managers一次性、可重复使用和可重入的上下文管理器¶
Most context managers are written in a way that means they can only be used effectively in a 大多数上下文管理器的编写方式意味着它们只能在with
statement once. with
语句中有效使用一次。These single use context managers must be created afresh each time they’re used - attempting to use them a second time will trigger an exception or otherwise not work correctly.每次使用这些一次性上下文管理器时,都必须重新创建它们——试图再次使用它们将引发异常或无法正常工作。
This common limitation means that it is generally advisable to create context managers directly in the header of the 这种常见的限制意味着,通常建议直接在使用上下文管理器的with
statement where they are used (as shown in all of the usage examples above).with
语句的头中创建上下文管理器(如上面所有的使用示例所示)。
Files are an example of effectively single use context managers, since the first 文件是有效的一次性上下文管理器的一个例子,因为第一个with
statement will close the file, preventing any further IO operations using that file object.with
语句将关闭文件,防止使用该文件对象进行任何进一步的IO操作。
Context managers created using 使用contextmanager()
are also single use context managers, and will complain about the underlying generator failing to yield if an attempt is made to use them a second time:contextmanager()
创建的上下文管理器也是一次性上下文管理器,如果试图再次使用它们,则会抱怨底层生成器无法生成:
>>> from contextlib import contextmanager
>>> @contextmanager
... def singleuse():
... print("Before")
... yield
... print("After")
...
>>> cm = singleuse()
>>> with cm:
... pass
...
Before
After
>>> with cm:
... pass
...
Traceback (most recent call last):
...
RuntimeError: generator didn't yield
Reentrant context managers重新输入上下文管理器¶
More sophisticated context managers may be “reentrant”. 更复杂的上下文管理器可能是“可重入的”。These context managers can not only be used in multiple 这些上下文管理器不仅可以在多个with
statements, but may also be used inside a with
statement that is already using the same context manager.with
语句中使用,还可以在已经使用同一上下文管理器的with
语句内部使用。
threading.RLock
is an example of a reentrant context manager, as are 是可重入上下文管理器的一个示例,suppress()
and redirect_stdout()
. suppress()
和redirect_stdout()
也是如此。Here’s a very simple example of reentrant use:下面是一个非常简单的可重入使用示例:
>>> from contextlib import redirect_stdout
>>> from io import StringIO
>>> stream = StringIO()
>>> write_to_stream = redirect_stdout(stream)
>>> with write_to_stream:
... print("This is written to the stream rather than stdout")
... with write_to_stream:
... print("This is also written to the stream")
...
>>> print("This is written directly to stdout")
This is written directly to stdout
>>> print(stream.getvalue())
This is written to the stream rather than stdout
This is also written to the stream
Real world examples of reentrancy are more likely to involve multiple functions calling each other and hence be far more complicated than this example.现实世界中的可重入性示例更有可能涉及多个函数相互调用,因此比本示例复杂得多。
Note also that being reentrant is not the same thing as being thread safe. 还要注意,可重入性与线程安全性是不一样的。例如,redirect_stdout()
, for example, is definitely not thread safe, as it makes a global modification to the system state by binding sys.stdout
to a different stream.redirect_stdout()
肯定不是线程安全的,因为它通过将sys.stdout
绑定到不同的流来全局修改系统状态。
Reusable context managers可重用的上下文管理器¶
Distinct from both single use and reentrant context managers are “reusable” context managers (or, to be completely explicit, “reusable, but not reentrant” context managers, since reentrant context managers are also reusable). 与一次性和可重入上下文管理器不同的是“可重用”上下文管理器(或者,完全明确地说,“可重用但不可重入”上下文管理者,因为可重入的上下文管理器也是可重用的)。These context managers support being used multiple times, but will fail (or otherwise not work correctly) if the specific context manager instance has already been used in a containing with statement.这些上下文管理器支持多次使用,但如果特定的上下文管理器实例已在containing with语句中使用,则会失败(或无法正常工作)。
threading.Lock
is an example of a reusable, but not reentrant, context manager (for a reentrant lock, it is necessary to use 是一个可重用但不可重入的上下文管理器的示例(对于重入锁,有必要使用threading.RLock
instead).threading.RLock
)。
Another example of a reusable, but not reentrant, context manager is 可重复使用但不可重入的上下文管理器的另一个示例是ExitStack
, as it invokes all currently registered callbacks when leaving any with statement, regardless of where those callbacks were added:ExitStack
,因为无论这些回调是在哪里添加的,它都会在留下任何with
语句时调用所有当前注册的回调:
>>> from contextlib import ExitStack
>>> stack = ExitStack()
>>> with stack:
... stack.callback(print, "Callback: from first context")
... print("Leaving first context")
...
Leaving first context
Callback: from first context
>>> with stack:
... stack.callback(print, "Callback: from second context")
... print("Leaving second context")
...
Leaving second context
Callback: from second context
>>> with stack:
... stack.callback(print, "Callback: from outer context")
... with stack:
... stack.callback(print, "Callback: from inner context")
... print("Leaving inner context")
... print("Leaving outer context")
...
Leaving inner context
Callback: from inner context
Callback: from outer context
Leaving outer context
As the output from the example shows, reusing a single stack object across multiple with statements works correctly, but attempting to nest them will cause the stack to be cleared at the end of the innermost with statement, which is unlikely to be desirable behaviour.正如示例的输出所示,在多个with语句中重用单个堆栈对象是正确的,但试图嵌套它们会导致堆栈在最里面的with语句末尾被清除,这不太可能是理想的行为。
Using separate 使用单独的ExitStack
instances instead of reusing a single instance avoids that problem:ExitStack
实例而不是重用单个实例可以避免该问题:
>>> from contextlib import ExitStack
>>> with ExitStack() as outer_stack:
... outer_stack.callback(print, "Callback: from outer context")
... with ExitStack() as inner_stack:
... inner_stack.callback(print, "Callback: from inner context")
... print("Leaving inner context")
... print("Leaving outer context")
...
Leaving inner context
Callback: from inner context
Leaving outer context
Callback: from outer context