“Python/docs/3.9/library/contextlib”的版本间差异

来自菜鸟教程
Python/docs/3.9/library/contextlib
跳转至:导航、​搜索
(autoload)
 
(Page commit)
 
第1行: 第1行:
 +
{{DISPLAYTITLE:contextlib — with 语句上下文的实用程序 — Python 文档}}
 
<div id="module-contextlib" class="section">
 
<div id="module-contextlib" class="section">
  
 
<span id="contextlib-utilities-for-with-statement-contexts"></span>
 
<span id="contextlib-utilities-for-with-statement-contexts"></span>
= <code>contextlib</code> --- Utilities for <code>with</code>-statement contexts =
+
= contextlib with 语句上下文的实用程序 =
  
'''Source code:''' [https://github.com/python/cpython/tree/3.9/Lib/contextlib.py Lib/contextlib.py]
+
'''源代码:''' [[#id1|<span id="id2" class="problematic">:source:`Lib/contextlib.py`</span>]]
  
This module provides utilities for common tasks involving the [[../../reference/compound_stmts#with|<code>with</code>]]
+
 
statement. For more information see also [[../stdtypes#typecontextmanager|<span class="std std-ref">Context Manager Types</span>]] and
+
-----
[[../../reference/datamodel#context-managers|<span class="std std-ref">With Statement Context Managers</span>]].
+
 
 +
该模块为涉及 [[../../reference/compound_stmts#with|with]] 语句的常见任务提供实用程序。 有关更多信息,另请参阅 [[../stdtypes#typecontextmanager|上下文管理器类型]] [[../../reference/datamodel#context-managers|带有语句上下文管理器]]
  
 
<div id="utilities" class="section">
 
<div id="utilities" class="section">
  
== Utilities ==
+
== 公用程序 ==
  
Functions and classes provided:
+
提供的函数和类:
  
 
<dl>
 
<dl>
<dt>''class'' <code>contextlib.</code><code>AbstractContextManager</code></dt>
+
<dt>''<span class="pre">class</span>'' <span class="sig-prename descclassname"><span class="pre">contextlib.</span></span><span class="sig-name descname"><span class="pre">AbstractContextManager</span></span></dt>
<dd><p>An [[../../glossary#term-abstract-base-class|<span class="xref std std-term">abstract base class</span>]] for classes that implement
+
<dd><p>实现 [[../../reference/datamodel#object|object.__enter__()]] [[../../reference/datamodel#object|object.__exit__()]] 的类的 [[../../glossary#term-abstract-base-class|抽象基类]] 。 提供了 [[../../reference/datamodel#object|object.__enter__()]] 的默认实现,它返回 <code>self</code> [[../../reference/datamodel#object|object.__exit__()]] 是一个抽象方法,默认返回 <code>None</code> ]。 另请参阅 [[../stdtypes#typecontextmanager|上下文管理器类型]] 的定义。</p>
[[../../reference/datamodel#object|<code>object.__enter__()</code>]] and [[../../reference/datamodel#object|<code>object.__exit__()</code>]]. A default
 
implementation for [[../../reference/datamodel#object|<code>object.__enter__()</code>]] is provided which returns
 
<code>self</code> while [[../../reference/datamodel#object|<code>object.__exit__()</code>]] is an abstract method which by default
 
returns <code>None</code>. See also the definition of [[../stdtypes#typecontextmanager|<span class="std std-ref">Context Manager Types</span>]].</p>
 
 
<div class="versionadded">
 
<div class="versionadded">
  
<p><span class="versionmodified added">3.6 新版功能.</span></p>
+
<p><span class="versionmodified added">3.6 版中的新功能。</span></p>
  
 
</div></dd></dl>
 
</div></dd></dl>
  
 
<dl>
 
<dl>
<dt>''class'' <code>contextlib.</code><code>AbstractAsyncContextManager</code></dt>
+
<dt>''<span class="pre">class</span>'' <span class="sig-prename descclassname"><span class="pre">contextlib.</span></span><span class="sig-name descname"><span class="pre">AbstractAsyncContextManager</span></span></dt>
<dd><p>An [[../../glossary#term-abstract-base-class|<span class="xref std std-term">abstract base class</span>]] for classes that implement
+
<dd><p>实现 [[../../reference/datamodel#object|object.__aenter__()]] [[../../reference/datamodel#object|object.__aexit__()]] 的类的 [[../../glossary#term-abstract-base-class|抽象基类]] 。 提供了 [[../../reference/datamodel#object|object.__aenter__()]] 的默认实现,它返回 <code>self</code> [[../../reference/datamodel#object|object.__aexit__()]] 是一个抽象方法,默认返回 <code>None</code> ]。 另请参阅 [[../../reference/datamodel#async-context-managers|异步上下文管理器]] 的定义。</p>
[[../../reference/datamodel#object|<code>object.__aenter__()</code>]] and [[../../reference/datamodel#object|<code>object.__aexit__()</code>]]. A default
 
implementation for [[../../reference/datamodel#object|<code>object.__aenter__()</code>]] is provided which returns
 
<code>self</code> while [[../../reference/datamodel#object|<code>object.__aexit__()</code>]] is an abstract method which by default
 
returns <code>None</code>. See also the definition of
 
[[../../reference/datamodel#async-context-managers|<span class="std std-ref">Asynchronous Context Managers</span>]].</p>
 
 
<div class="versionadded">
 
<div class="versionadded">
  
<p><span class="versionmodified added">3.7 新版功能.</span></p>
+
<p><span class="versionmodified added">3.7 版中的新功能。</span></p>
  
 
</div></dd></dl>
 
</div></dd></dl>
  
 
<dl>
 
<dl>
<dt><code>@</code><code>contextlib.</code><code>contextmanager</code></dt>
+
<dt><span class="sig-prename descclassname"><span class="pre">@</span></span><span class="sig-prename descclassname"><span class="pre">contextlib.</span></span><span class="sig-name descname"><span class="pre">contextmanager</span></span></dt>
<dd><p>This function is a [[../../glossary#term-decorator|<span class="xref std std-term">decorator</span>]] that can be used to define a factory
+
<dd><p>该函数是一个[[../../glossary#term-decorator|装饰器]],可用于为[[../../reference/compound_stmts#with|]]语句上下文管理器定义工厂函数,无需创建类或将<code>__enter__()</code><code>__exit__()</code> 方法。</p>
function for [[../../reference/compound_stmts#with|<code>with</code>]] statement context managers, without needing to
+
<p>虽然许多对象本身支持在 with 语句中使用,但有时需要管理的资源本身不是上下文管理器,并且不实现 <code>contextlib.closing</code> <code>close()</code> 方法]</p>
create a class or separate <code>__enter__()</code> and <code>__exit__()</code> methods.</p>
+
<p>以下是一个抽象示例,以确保正确的资源管理:</p>
<p>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 <code>close()</code> method for use with <code>contextlib.closing</code></p>
 
<p>An abstract example would be the following to ensure correct resource
 
management:</p>
 
 
<div class="highlight-python3 notranslate">
 
<div class="highlight-python3 notranslate">
  
 
<div class="highlight">
 
<div class="highlight">
  
<pre>from contextlib import contextmanager
+
<syntaxhighlight lang="python3">from contextlib import contextmanager
  
 
@contextmanager
 
@contextmanager
第69行: 第57行:
 
         release_resource(resource)
 
         release_resource(resource)
  
&gt;&gt;&gt; with managed_resource(timeout=3600) as resource:
+
>>> with managed_resource(timeout=3600) as resource:
 
...    # Resource is released at the end of this block,
 
...    # Resource is released at the end of this block,
...    # even if code in the block raises an exception</pre>
+
...    # even if code in the block raises an exception</syntaxhighlight>
  
 
</div>
 
</div>
  
 
</div>
 
</div>
<p>The function being decorated must return a [[../../glossary#term-generator|<span class="xref std std-term">generator</span>]]-iterator when
+
<p>被装饰的函数在调用时必须返回一个 [[../../glossary#term-generator|generator]]-iterator。 这个迭代器必须正好产生一个值,该值将绑定到 [[../../reference/compound_stmts#with|with]] 语句的 <code>as</code> 子句中的目标,如果有的话。</p>
called. This iterator must yield exactly one value, which will be bound to
+
<p>在生成器产生时,嵌套在 [[../../reference/compound_stmts#with|with]] 语句中的块被执行。 然后在退出块后恢复生成器。 如果块中发生未处理的异常,它会在生成器内在产生的点处重新引发。 因此,您可以使用 [[../../reference/compound_stmts#try|try]]...[[../../reference/compound_stmts#except|except]]...[[../../reference/compound_stmts#finally|finally]] 语句来捕获错误(如果有),或确保进行一些清理。 如果一个异常被捕获只是为了记录它或执行某些操作(而不是完全抑制它),则生成器必须重新引发该异常。 否则,生成器上下文管理器将向 <code>with</code> 语句指示异常已被处理,并且将继续执行紧跟在 <code>with</code> 语句之后的语句。</p>
the targets in the [[../../reference/compound_stmts#with|<code>with</code>]] statement's <code>as</code> clause, if any.</p>
+
<p>[[#contextlib.contextmanager|contextmanager()]] 使用 [[#contextlib.ContextDecorator|ContextDecorator]] 因此它创建的上下文管理器可以用作装饰器以及在 [[../../reference/compound_stmts#with|with]] 语句中。 当用作装饰器时,每个函数调用都会隐式创建一个新的生成器实例(这允许由 [[#contextlib.contextmanager|contextmanager()]] 创建的“一次性”上下文管理器满足上下文管理器支持多次调用的要求以便用作装饰器)。</p>
<p>At the point where the generator yields, the block nested in the [[../../reference/compound_stmts#with|<code>with</code>]]
 
statement is executed. 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
 
[[../../reference/compound_stmts#try|<code>try</code>]]...[[../../reference/compound_stmts#except|<code>except</code>]]...[[../../reference/compound_stmts#finally|<code>finally</code>]] statement to trap
 
the error (if any), or ensure that some cleanup takes place. 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 <code>with</code> statement that
 
the exception has been handled, and execution will resume with the statement
 
immediately following the <code>with</code> statement.</p>
 
<p>[[#contextlib.contextmanager|<code>contextmanager()</code>]] uses [[#contextlib.ContextDecorator|<code>ContextDecorator</code>]] so the context managers
 
it creates can be used as decorators as well as in [[../../reference/compound_stmts#with|<code>with</code>]] statements.
 
When used as a decorator, a new generator instance is implicitly created on
 
each function call (this allows the otherwise &quot;one-shot&quot; context managers
 
created by [[#contextlib.contextmanager|<code>contextmanager()</code>]] to meet the requirement that context
 
managers support multiple invocations in order to be used as decorators).</p>
 
 
<div class="versionchanged">
 
<div class="versionchanged">
  
<p><span class="versionmodified changed">3.2 版更改: </span>Use of [[#contextlib.ContextDecorator|<code>ContextDecorator</code>]].</p>
+
<p><span class="versionmodified changed"> 3.2 版更改:</span> [[#contextlib.ContextDecorator|ContextDecorator]] 的使用。</p>
  
 
</div></dd></dl>
 
</div></dd></dl>
  
 
<dl>
 
<dl>
<dt><code>@</code><code>contextlib.</code><code>asynccontextmanager</code></dt>
+
<dt><span class="sig-prename descclassname"><span class="pre">@</span></span><span class="sig-prename descclassname"><span class="pre">contextlib.</span></span><span class="sig-name descname"><span class="pre">asynccontextmanager</span></span></dt>
<dd><p>Similar to [[#contextlib.contextmanager|<code>contextmanager()</code>]], but creates an
+
<dd><p>类似于 [[#contextlib.contextmanager|contextmanager()]],但创建一个 [[../../reference/datamodel#async-context-managers|异步上下文管理器]] </p>
[[../../reference/datamodel#async-context-managers|<span class="std std-ref">asynchronous context manager</span>]].</p>
+
<p>该函数是一个[[../../glossary#term-decorator|装饰器]],可用于为[[../../reference/compound_stmts#async-with|async with]]语句异步上下文管理器定义工厂函数,无需创建类或将<code>__aenter__()</code>和[ X209X] 方法。 它必须应用于 [[../../glossary#term-asynchronous-generator|异步发电机]] 功能。</p>
<p>This function is a [[../../glossary#term-decorator|<span class="xref std std-term">decorator</span>]] that can be used to define a factory
+
<p>一个简单的例子:</p>
function for [[../../reference/compound_stmts#async-with|<code>async with</code>]] statement asynchronous context managers,
 
without needing to create a class or separate <code>__aenter__()</code> and
 
<code>__aexit__()</code> methods. It must be applied to an [[../../glossary#term-asynchronous-generator|<span class="xref std std-term">asynchronous
 
generator</span>]] function.</p>
 
<p>A simple example:</p>
 
 
<div class="highlight-python3 notranslate">
 
<div class="highlight-python3 notranslate">
  
 
<div class="highlight">
 
<div class="highlight">
  
<pre>from contextlib import asynccontextmanager
+
<syntaxhighlight lang="python3">from contextlib import asynccontextmanager
  
 
@asynccontextmanager
 
@asynccontextmanager
第128行: 第94行:
 
async def get_all_users():
 
async def get_all_users():
 
     async with get_connection() as conn:
 
     async with get_connection() as conn:
         return conn.query('SELECT ...')</pre>
+
         return conn.query('SELECT ...')</syntaxhighlight>
  
 
</div>
 
</div>
第135行: 第101行:
 
<div class="versionadded">
 
<div class="versionadded">
  
<p><span class="versionmodified added">3.7 新版功能.</span></p>
+
<p><span class="versionmodified added">3.7 版中的新功能。</span></p>
  
 
</div></dd></dl>
 
</div></dd></dl>
  
 
<dl>
 
<dl>
<dt><code>contextlib.</code><code>closing</code><span class="sig-paren">(</span>''<span class="n">thing</span>''<span class="sig-paren">)</span></dt>
+
<dt><span class="sig-prename descclassname"><span class="pre">contextlib.</span></span><span class="sig-name descname"><span class="pre">closing</span></span><span class="sig-paren">(</span>''<span class="n"><span class="pre">thing</span></span>''<span class="sig-paren">)</span></dt>
<dd><p>Return a context manager that closes ''thing'' upon completion of the block. This
+
<dd><p>返回一个上下文管理器,在块完成时关闭 ''thing''。 这基本上相当于:</p>
is basically equivalent to:</p>
 
 
<div class="highlight-python3 notranslate">
 
<div class="highlight-python3 notranslate">
  
 
<div class="highlight">
 
<div class="highlight">
  
<pre>from contextlib import contextmanager
+
<syntaxhighlight lang="python3">from contextlib import contextmanager
  
 
@contextmanager
 
@contextmanager
第154行: 第119行:
 
         yield thing
 
         yield thing
 
     finally:
 
     finally:
         thing.close()</pre>
+
         thing.close()</syntaxhighlight>
  
 
</div>
 
</div>
  
 
</div>
 
</div>
<p>And lets you write code like this:</p>
+
<p>并让您编写这样的代码:</p>
 
<div class="highlight-python3 notranslate">
 
<div class="highlight-python3 notranslate">
  
 
<div class="highlight">
 
<div class="highlight">
  
<pre>from contextlib import closing
+
<syntaxhighlight lang="python3">from contextlib import closing
 
from urllib.request import urlopen
 
from urllib.request import urlopen
  
with closing(urlopen('http://www.python.org')) as page:
+
with closing(urlopen('https://www.python.org')) as page:
 
     for line in page:
 
     for line in page:
         print(line)</pre>
+
         print(line)</syntaxhighlight>
  
 
</div>
 
</div>
  
 
</div>
 
</div>
<p>without needing to explicitly close <code>page</code>. Even if an error occurs,
+
<p>无需明确关闭 <code>page</code>。 即使发生错误,退出[[../../reference/compound_stmts#with|with]]块时也会调用<code>page.close()</code></p></dd></dl>
<code>page.close()</code> will be called when the [[../../reference/compound_stmts#with|<code>with</code>]] block is exited.</p></dd></dl>
 
  
 
<span id="simplifying-support-for-single-optional-context-managers" class="target"></span>
 
<span id="simplifying-support-for-single-optional-context-managers" class="target"></span>
 
<dl>
 
<dl>
<dt><code>contextlib.</code><code>nullcontext</code><span class="sig-paren">(</span>''<span class="n">enter_result</span><span class="o">=</span><span class="default_value">None</span>''<span class="sig-paren">)</span></dt>
+
<dt><span class="sig-prename descclassname"><span class="pre">contextlib.</span></span><span class="sig-name descname"><span class="pre">nullcontext</span></span><span class="sig-paren">(</span>''<span class="n"><span class="pre">enter_result</span></span><span class="o"><span class="pre">=</span></span><span class="default_value"><span class="pre">None</span></span>''<span class="sig-paren">)</span></dt>
<dd><p>Return a context manager that returns ''enter_result'' from <code>__enter__</code>, but
+
<dd><p>返回一个从 <code>__enter__</code> 返回 ''enter_result'' 的上下文管理器,否则什么都不做。 它旨在用作可选上下文管理器的替代品,例如:</p>
otherwise does nothing. It is intended to be used as a stand-in for an
 
optional context manager, for example:</p>
 
 
<div class="highlight-python3 notranslate">
 
<div class="highlight-python3 notranslate">
  
 
<div class="highlight">
 
<div class="highlight">
  
<pre>def myfunction(arg, ignore_exceptions=False):
+
<syntaxhighlight lang="python3">def myfunction(arg, ignore_exceptions=False):
 
     if ignore_exceptions:
 
     if ignore_exceptions:
 
         # Use suppress to ignore all exceptions.
 
         # Use suppress to ignore all exceptions.
第195行: 第157行:
 
         cm = contextlib.nullcontext()
 
         cm = contextlib.nullcontext()
 
     with cm:
 
     with cm:
         # Do something</pre>
+
         # Do something</syntaxhighlight>
  
 
</div>
 
</div>
  
 
</div>
 
</div>
<p>An example using ''enter_result'':</p>
+
<p>使用 ''enter_result'' 的示例:</p>
 
<div class="highlight-python3 notranslate">
 
<div class="highlight-python3 notranslate">
  
 
<div class="highlight">
 
<div class="highlight">
  
<pre>def process_file(file_or_path):
+
<syntaxhighlight lang="python3">def process_file(file_or_path):
 
     if isinstance(file_or_path, str):
 
     if isinstance(file_or_path, str):
 
         # If string, open file
 
         # If string, open file
第214行: 第176行:
  
 
     with cm as file:
 
     with cm as file:
         # Perform processing on the file</pre>
+
         # Perform processing on the file</syntaxhighlight>
  
 
</div>
 
</div>
第221行: 第183行:
 
<div class="versionadded">
 
<div class="versionadded">
  
<p><span class="versionmodified added">3.7 新版功能.</span></p>
+
<p><span class="versionmodified added">3.7 版中的新功能。</span></p>
  
 
</div></dd></dl>
 
</div></dd></dl>
  
 
<dl>
 
<dl>
<dt><code>contextlib.</code><code>suppress</code><span class="sig-paren">(</span>''<span class="o">*</span><span class="n">exceptions</span>''<span class="sig-paren">)</span></dt>
+
<dt><span class="sig-prename descclassname"><span class="pre">contextlib.</span></span><span class="sig-name descname"><span class="pre">suppress</span></span><span class="sig-paren">(</span>''<span class="o"><span class="pre">*</span></span><span class="n"><span class="pre">exceptions</span></span>''<span class="sig-paren">)</span></dt>
<dd><p>Return a context manager that suppresses any of the specified exceptions
+
<dd><p>返回一个上下文管理器,如果它们发生在 <code>with</code> 语句的主体中,则抑制任何指定的异常,然后使用 <code>with</code> 语句结束后的第一个语句恢复执行。</p>
if they occur in the body of a with statement and then resumes execution
+
<p>与完全抑制异常的任何其他机制一样,此上下文管理器应仅用于覆盖非常特定的错误,其中已知静默继续执行程序是正确的做法。</p>
with the first statement following the end of the with statement.</p>
+
<p>例如:</p>
<p>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.</p>
 
<p>For example:</p>
 
 
<div class="highlight-python3 notranslate">
 
<div class="highlight-python3 notranslate">
  
 
<div class="highlight">
 
<div class="highlight">
  
<pre>from contextlib import suppress
+
<syntaxhighlight lang="python3">from contextlib import suppress
  
 
with suppress(FileNotFoundError):
 
with suppress(FileNotFoundError):
第245行: 第202行:
  
 
with suppress(FileNotFoundError):
 
with suppress(FileNotFoundError):
     os.remove('someotherfile.tmp')</pre>
+
     os.remove('someotherfile.tmp')</syntaxhighlight>
  
 
</div>
 
</div>
  
 
</div>
 
</div>
<p>This code is equivalent to:</p>
+
<p>此代码等效于:</p>
 
<div class="highlight-python3 notranslate">
 
<div class="highlight-python3 notranslate">
  
 
<div class="highlight">
 
<div class="highlight">
  
<pre>try:
+
<syntaxhighlight lang="python3">try:
 
     os.remove('somefile.tmp')
 
     os.remove('somefile.tmp')
 
except FileNotFoundError:
 
except FileNotFoundError:
第263行: 第220行:
 
     os.remove('someotherfile.tmp')
 
     os.remove('someotherfile.tmp')
 
except FileNotFoundError:
 
except FileNotFoundError:
     pass</pre>
+
     pass</syntaxhighlight>
  
 
</div>
 
</div>
  
 
</div>
 
</div>
<p>This context manager is [[#reentrant-cms|<span class="std std-ref">reentrant</span>]].</p>
+
<p>这个上下文管理器是 [[#reentrant-cms|可重入的]] </p>
 
<div class="versionadded">
 
<div class="versionadded">
  
<p><span class="versionmodified added">3.4 新版功能.</span></p>
+
<p><span class="versionmodified added">3.4 版中的新功能。</span></p>
  
 
</div></dd></dl>
 
</div></dd></dl>
  
 
<dl>
 
<dl>
<dt><code>contextlib.</code><code>redirect_stdout</code><span class="sig-paren">(</span>''<span class="n">new_target</span>''<span class="sig-paren">)</span></dt>
+
<dt><span class="sig-prename descclassname"><span class="pre">contextlib.</span></span><span class="sig-name descname"><span class="pre">redirect_stdout</span></span><span class="sig-paren">(</span>''<span class="n"><span class="pre">new_target</span></span>''<span class="sig-paren">)</span></dt>
<dd><p>Context manager for temporarily redirecting [[../sys#sys|<code>sys.stdout</code>]] to
+
<dd><p>上下文管理器,用于临时将 [[../sys#sys|sys.stdout]] 重定向到另一个文件或类似文件的对象。</p>
another file or file-like object.</p>
+
<p>此工具为输出硬连线到标准输出的现有函数或类增加了灵活性。</p>
<p>This tool adds flexibility to existing functions or classes whose output
+
<p>例如,[[../functions#help|help()]] 的输出通常被发送到 ''sys.stdout''。 您可以通过将输出重定向到 [[../io#io|io.StringIO]] 对象来捕获字符串中的输出。 替换流从 <code>__enter__</code> 方法返回,因此可用作 [[../../reference/compound_stmts#with|with]] 语句的目标:</p>
is hardwired to stdout.</p>
 
<p>For example, the output of [[../functions#help|<code>help()</code>]] normally is sent to ''sys.stdout''.
 
You can capture that output in a string by redirecting the output to an
 
[[../io#io|<code>io.StringIO</code>]] object:</p>
 
 
<div class="highlight-python3 notranslate">
 
<div class="highlight-python3 notranslate">
  
 
<div class="highlight">
 
<div class="highlight">
  
<pre>f = io.StringIO()
+
<syntaxhighlight lang="python3">with redirect_stdout(io.StringIO()) as f:
with redirect_stdout(f):
 
 
     help(pow)
 
     help(pow)
s = f.getvalue()</pre>
+
s = f.getvalue()</syntaxhighlight>
  
 
</div>
 
</div>
  
 
</div>
 
</div>
<p>To send the output of [[../functions#help|<code>help()</code>]] to a file on disk, redirect the output
+
<p>要将 [[../functions#help|help()]] 的输出发送到磁盘上的文件,请将输出重定向到常规文件:</p>
to a regular file:</p>
 
 
<div class="highlight-python3 notranslate">
 
<div class="highlight-python3 notranslate">
  
 
<div class="highlight">
 
<div class="highlight">
  
<pre>with open('help.txt', 'w') as f:
+
<syntaxhighlight lang="python3">with open('help.txt', 'w') as f:
 
     with redirect_stdout(f):
 
     with redirect_stdout(f):
         help(pow)</pre>
+
         help(pow)</syntaxhighlight>
  
 
</div>
 
</div>
  
 
</div>
 
</div>
<p>To send the output of [[../functions#help|<code>help()</code>]] to ''sys.stderr'':</p>
+
<p>[[../functions#help|help()]] 的输出发送到 ''sys.stderr''</p>
 
<div class="highlight-python3 notranslate">
 
<div class="highlight-python3 notranslate">
  
 
<div class="highlight">
 
<div class="highlight">
  
<pre>with redirect_stdout(sys.stderr):
+
<syntaxhighlight lang="python3">with redirect_stdout(sys.stderr):
     help(pow)</pre>
+
     help(pow)</syntaxhighlight>
  
 
</div>
 
</div>
  
 
</div>
 
</div>
<p>Note that the global side effect on [[../sys#sys|<code>sys.stdout</code>]] means that this
+
<p>请注意,对 [[../sys#sys|sys.stdout]] 的全局副作用意味着此上下文管理器不适合在库代码和大多数线程应用程序中使用。 它也对子流程的输出没有影响。 但是,对于许多实用程序脚本来说,它仍然是一种有用的方法。</p>
context manager is not suitable for use in library code and most threaded
+
<p>这个上下文管理器是 [[#reentrant-cms|可重入的]] </p>
applications. It also has no effect on the output of subprocesses.
 
However, it is still a useful approach for many utility scripts.</p>
 
<p>This context manager is [[#reentrant-cms|<span class="std std-ref">reentrant</span>]].</p>
 
 
<div class="versionadded">
 
<div class="versionadded">
  
<p><span class="versionmodified added">3.4 新版功能.</span></p>
+
<p><span class="versionmodified added">3.4 版中的新功能。</span></p>
  
 
</div></dd></dl>
 
</div></dd></dl>
  
 
<dl>
 
<dl>
<dt><code>contextlib.</code><code>redirect_stderr</code><span class="sig-paren">(</span>''<span class="n">new_target</span>''<span class="sig-paren">)</span></dt>
+
<dt><span class="sig-prename descclassname"><span class="pre">contextlib.</span></span><span class="sig-name descname"><span class="pre">redirect_stderr</span></span><span class="sig-paren">(</span>''<span class="n"><span class="pre">new_target</span></span>''<span class="sig-paren">)</span></dt>
<dd><p>Similar to [[#contextlib.redirect_stdout|<code>redirect_stdout()</code>]] but redirecting
+
<dd><p>类似于 [[#contextlib.redirect_stdout|redirect_stdout()]] 但将 [[../sys#sys|sys.stderr]] 重定向到另一个文件或类似文件的对象。</p>
[[../sys#sys|<code>sys.stderr</code>]] to another file or file-like object.</p>
+
<p>这个上下文管理器是 [[#reentrant-cms|可重入的]] </p>
<p>This context manager is [[#reentrant-cms|<span class="std std-ref">reentrant</span>]].</p>
 
 
<div class="versionadded">
 
<div class="versionadded">
  
<p><span class="versionmodified added">3.5 新版功能.</span></p>
+
<p><span class="versionmodified added">3.5 版中的新功能。</span></p>
  
 
</div></dd></dl>
 
</div></dd></dl>
  
 
<dl>
 
<dl>
<dt>''class'' <code>contextlib.</code><code>ContextDecorator</code></dt>
+
<dt>''<span class="pre">class</span>'' <span class="sig-prename descclassname"><span class="pre">contextlib.</span></span><span class="sig-name descname"><span class="pre">ContextDecorator</span></span></dt>
<dd><p>A base class that enables a context manager to also be used as a decorator.</p>
+
<dd><p>使上下文管理器也可以用作装饰器的基类。</p>
<p>Context managers inheriting from <code>ContextDecorator</code> have to implement
+
<p>继承自 <code>ContextDecorator</code> 的上下文管理器必须照常实现 <code>__enter__</code> <code>__exit__</code><code>__exit__</code> 即使用作装饰器也保留其可选的异常处理。</p>
<code>__enter__</code> and <code>__exit__</code> as normal. <code>__exit__</code> retains its optional
+
<p><code>ContextDecorator</code> [[#contextlib.contextmanager|contextmanager()]] 使用,因此您会自动获得此功能。</p>
exception handling even when used as a decorator.</p>
+
<p><code>ContextDecorator</code> 示例:</p>
<p><code>ContextDecorator</code> is used by [[#contextlib.contextmanager|<code>contextmanager()</code>]], so you get this
 
functionality automatically.</p>
 
<p>Example of <code>ContextDecorator</code>:</p>
 
 
<div class="highlight-python3 notranslate">
 
<div class="highlight-python3 notranslate">
  
 
<div class="highlight">
 
<div class="highlight">
  
<pre>from contextlib import ContextDecorator
+
<syntaxhighlight lang="python3">from contextlib import ContextDecorator
  
 
class mycontext(ContextDecorator):
 
class mycontext(ContextDecorator):
第366行: 第310行:
 
         return False
 
         return False
  
&gt;&gt;&gt; @mycontext()
+
>>> @mycontext()
 
... def function():
 
... def function():
 
...    print('The bit in the middle')
 
...    print('The bit in the middle')
 
...
 
...
&gt;&gt;&gt; function()
+
>>> function()
 
Starting
 
Starting
 
The bit in the middle
 
The bit in the middle
 
Finishing
 
Finishing
  
&gt;&gt;&gt; with mycontext():
+
>>> with mycontext():
 
...    print('The bit in the middle')
 
...    print('The bit in the middle')
 
...
 
...
 
Starting
 
Starting
 
The bit in the middle
 
The bit in the middle
Finishing</pre>
+
Finishing</syntaxhighlight>
  
 
</div>
 
</div>
  
 
</div>
 
</div>
<p>This change is just syntactic sugar for any construct of the following form:</p>
+
<p>此更改只是以下形式的任何构造的语法糖:</p>
 
<div class="highlight-python3 notranslate">
 
<div class="highlight-python3 notranslate">
  
 
<div class="highlight">
 
<div class="highlight">
  
<pre>def f():
+
<syntaxhighlight lang="python3">def f():
 
     with cm():
 
     with cm():
         # Do stuff</pre>
+
         # Do stuff</syntaxhighlight>
  
 
</div>
 
</div>
  
 
</div>
 
</div>
<p><code>ContextDecorator</code> lets you instead write:</p>
+
<p><code>ContextDecorator</code> 让你改写:</p>
 
<div class="highlight-python3 notranslate">
 
<div class="highlight-python3 notranslate">
  
 
<div class="highlight">
 
<div class="highlight">
  
<pre>@cm()
+
<syntaxhighlight lang="python3">@cm()
 
def f():
 
def f():
     # Do stuff</pre>
+
     # Do stuff</syntaxhighlight>
  
 
</div>
 
</div>
  
 
</div>
 
</div>
<p>It makes it clear that the <code>cm</code> applies to the whole function, rather than
+
<p>它清楚地表明 <code>cm</code> 适用于整个函数,而不仅仅是它的一部分(并且保存缩进级别也很好)。</p>
just a piece of it (and saving an indentation level is nice, too).</p>
+
<p>已经有基类的现有上下文管理器可以通过使用 <code>ContextDecorator</code> 作为混合类来扩展:</p>
<p>Existing context managers that already have a base class can be extended by
 
using <code>ContextDecorator</code> as a mixin class:</p>
 
 
<div class="highlight-python3 notranslate">
 
<div class="highlight-python3 notranslate">
  
 
<div class="highlight">
 
<div class="highlight">
  
<pre>from contextlib import ContextDecorator
+
<syntaxhighlight lang="python3">from contextlib import ContextDecorator
  
 
class mycontext(ContextBaseClass, ContextDecorator):
 
class mycontext(ContextBaseClass, ContextDecorator):
第424行: 第366行:
  
 
     def __exit__(self, *exc):
 
     def __exit__(self, *exc):
         return False</pre>
+
         return False</syntaxhighlight>
  
 
</div>
 
</div>
第431行: 第373行:
 
<div class="admonition note">
 
<div class="admonition note">
  
<p>注解</p>
+
<p>笔记</p>
<p>As the decorated function must be able to be called multiple times, the
+
<p>由于修饰函数必须能够被多次调用,底层上下文管理器必须支持在多个 [[../../reference/compound_stmts#with|with]] 语句中使用。 如果不是这种情况,则应使用在函数内具有显式 <code>with</code> 语句的原始构造。</p>
underlying context manager must support use in multiple [[../../reference/compound_stmts#with|<code>with</code>]]
 
statements. If this is not the case, then the original construct with the
 
explicit <code>with</code> statement inside the function should be used.</p>
 
  
 
</div>
 
</div>
 
<div class="versionadded">
 
<div class="versionadded">
  
<p><span class="versionmodified added">3.2 新版功能.</span></p>
+
<p><span class="versionmodified added">3.2 版中的新功能。</span></p>
  
 
</div></dd></dl>
 
</div></dd></dl>
  
 
<dl>
 
<dl>
<dt>''class'' <code>contextlib.</code><code>ExitStack</code></dt>
+
<dt>''<span class="pre">class</span>'' <span class="sig-prename descclassname"><span class="pre">contextlib.</span></span><span class="sig-name descname"><span class="pre">ExitStack</span></span></dt>
<dd><p>A context manager that is designed to make it easy to programmatically
+
<dd><p>一个上下文管理器,旨在使以编程方式组合其他上下文管理器和清理功能变得容易,尤其是那些可选的或由输入数据驱动的。</p>
combine other context managers and cleanup functions, especially those
+
<p>例如,可以在单个 with 语句中轻松处理一组文件,如下所示:</p>
that are optional or otherwise driven by input data.</p>
 
<p>For example, a set of files may easily be handled in a single with
 
statement as follows:</p>
 
 
<div class="highlight-python3 notranslate">
 
<div class="highlight-python3 notranslate">
  
 
<div class="highlight">
 
<div class="highlight">
  
<pre>with ExitStack() as stack:
+
<syntaxhighlight lang="python3">with ExitStack() as stack:
 
     files = [stack.enter_context(open(fname)) for fname in filenames]
 
     files = [stack.enter_context(open(fname)) for fname in filenames]
 
     # All opened files will automatically be closed at the end of
 
     # All opened files will automatically be closed at the end of
 
     # the with statement, even if attempts to open files later
 
     # the with statement, even if attempts to open files later
     # in the list raise an exception</pre>
+
     # in the list raise an exception</syntaxhighlight>
  
 
</div>
 
</div>
  
 
</div>
 
</div>
<p>Each instance maintains a stack of registered callbacks that are called in
+
<p>每个实例都维护一个已注册的回调堆栈,当实例关闭时(在 [[../../reference/compound_stmts#with|with]] 语句的末尾显式或隐式地)调用这些回调的堆栈。 请注意,当上下文堆栈实例被垃圾收集时,回调不是 ''而不是'' 隐式调用。</p>
reverse order when the instance is closed (either explicitly or implicitly
+
<p>使用此堆栈模型,以便可以正确处理在其 <code>__init__</code> 方法(例如文件对象)中获取资源的上下文管理器。</p>
at the end of a [[../../reference/compound_stmts#with|<code>with</code>]] statement). Note that callbacks are ''not''
+
<p>由于注册的回调是以注册的相反顺序调用的,这最终表现为多个嵌套的 [[../../reference/compound_stmts#with|with]] 语句已与注册的回调集一起使用。 这甚至扩展到异常处理 - 如果内部回调抑制或替换异常,则外部回调将根据更新的状态传递参数。</p>
invoked implicitly when the context stack instance is garbage collected.</p>
+
<p>这是一个相对低级的 API,负责处理正确展开退出回调堆栈的细节。 它为更高级别的上下文管理器提供了合适的基础,这些管理器以特定于应用程序的方式操作出口堆栈。</p>
<p>This stack model is used so that context managers that acquire their
 
resources in their <code>__init__</code> method (such as file objects) can be
 
handled correctly.</p>
 
<p>Since registered callbacks are invoked in the reverse order of
 
registration, this ends up behaving as if multiple nested [[../../reference/compound_stmts#with|<code>with</code>]]
 
statements had been used with the registered set of callbacks. 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.</p>
 
<p>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.</p>
 
 
<div class="versionadded">
 
<div class="versionadded">
  
<p><span class="versionmodified added">3.3 新版功能.</span></p>
+
<p><span class="versionmodified added">3.3 版中的新功能。</span></p>
  
 
</div>
 
</div>
 
<dl>
 
<dl>
<dt><code>enter_context</code><span class="sig-paren">(</span>''<span class="n">cm</span>''<span class="sig-paren">)</span></dt>
+
<dt><span class="sig-name descname"><span class="pre">enter_context</span></span><span class="sig-paren">(</span>''<span class="n"><span class="pre">cm</span></span>''<span class="sig-paren">)</span></dt>
<dd><p>Enters a new context manager and adds its <code>__exit__()</code> method to
+
<dd><p>进入一个新的上下文管理器并将其 <code>__exit__()</code> 方法添加到回调堆栈中。 返回值是上下文管理器自己的 <code>__enter__()</code> 方法的结果。</p>
the callback stack. The return value is the result of the context
+
<p>这些上下文管理器可以抑制异常,就像它们通常直接用作 [[../../reference/compound_stmts#with|with]] 语句的一部分一样。</p></dd></dl>
manager's own <code>__enter__()</code> method.</p>
 
<p>These context managers may suppress exceptions just as they normally
 
would if used directly as part of a [[../../reference/compound_stmts#with|<code>with</code>]] statement.</p></dd></dl>
 
  
 
<dl>
 
<dl>
<dt><code>push</code><span class="sig-paren">(</span>''<span class="n">exit</span>''<span class="sig-paren">)</span></dt>
+
<dt><span class="sig-name descname"><span class="pre">push</span></span><span class="sig-paren">(</span>''<span class="n"><span class="pre">exit</span></span>''<span class="sig-paren">)</span></dt>
<dd><p>Adds a context manager's <code>__exit__()</code> method to the callback stack.</p>
+
<dd><p>将上下文管理器的 <code>__exit__()</code> 方法添加到回调堆栈。</p>
<p>As <code>__enter__</code> is ''not'' invoked, this method can be used to cover
+
<p>由于 <code>__enter__</code> 不是 ''而不是'' 调用,因此该方法可用于使用上下文管理器自己的 <code>__exit__()</code> 方法覆盖 <code>__enter__()</code> 实现的一部分。</p>
part of an <code>__enter__()</code> implementation with a context manager's own
+
<p>如果传递的对象不是上下文管理器,则此方法假定它是与上下文管理器的 <code>__exit__()</code> 方法具有相同签名的回调,并将其直接添加到回调堆栈中。</p>
<code>__exit__()</code> method.</p>
+
<p>通过返回真值,这些回调可以像上下文管理器 <code>__exit__()</code> 方法一样抑制异常。</p>
<p>If passed an object that is not a context manager, this method assumes
+
<p>传入的对象是从函数返回的,允许将此方法用作函数装饰器。</p></dd></dl>
it is a callback with the same signature as a context manager's
 
<code>__exit__()</code> method and adds it directly to the callback stack.</p>
 
<p>By returning true values, these callbacks can suppress exceptions the
 
same way context manager <code>__exit__()</code> methods can.</p>
 
<p>The passed in object is returned from the function, allowing this
 
method to be used as a function decorator.</p></dd></dl>
 
  
 
<dl>
 
<dl>
<dt><code>callback</code><span class="sig-paren">(</span>''<span class="n">callback</span>'', ''<span class="o">/</span>'', ''<span class="o">*</span><span class="n">args</span>'', ''<span class="o">**</span><span class="n">kwds</span>''<span class="sig-paren">)</span></dt>
+
<dt><span class="sig-name descname"><span class="pre">callback</span></span><span class="sig-paren">(</span>''<span class="n"><span class="pre">callback</span></span>'', ''<span class="o"><span class="pre">/</span></span>'', ''<span class="o"><span class="pre">*</span></span><span class="n"><span class="pre">args</span></span>'', ''<span class="o"><span class="pre">**</span></span><span class="n"><span class="pre">kwds</span></span>''<span class="sig-paren">)</span></dt>
<dd><p>Accepts an arbitrary callback function and arguments and adds it to
+
<dd><p>接受任意回调函数和参数并将其添加到回调堆栈中。</p>
the callback stack.</p>
+
<p>与其他方法不同,以这种方式添加的回调无法抑制异常(因为它们永远不会传递异常详细信息)。</p>
<p>Unlike the other methods, callbacks added this way cannot suppress
+
<p>传入的回调是从函数返回的,允许将此方法用作函数装饰器。</p></dd></dl>
exceptions (as they are never passed the exception details).</p>
 
<p>The passed in callback is returned from the function, allowing this
 
method to be used as a function decorator.</p></dd></dl>
 
  
 
<dl>
 
<dl>
<dt><code>pop_all</code><span class="sig-paren">(</span><span class="sig-paren">)</span></dt>
+
<dt><span class="sig-name descname"><span class="pre">pop_all</span></span><span class="sig-paren">(</span><span class="sig-paren">)</span></dt>
<dd><p>Transfers the callback stack to a fresh [[#contextlib.ExitStack|<code>ExitStack</code>]] instance
+
<dd><p>将回调堆栈传输到一个新的 [[#contextlib.ExitStack|ExitStack]] 实例并返回它。 此操作不会调用回调 - 相反,现在将在关闭新堆栈时调用它们(在 [[../../reference/compound_stmts#with|with]] 语句结束时显式或隐式)。</p>
and returns it. No callbacks are invoked by this operation - instead,
+
<p>例如,一组文件可以作为“全有或全无”操作打开,如下所示:</p>
they will now be invoked when the new stack is closed (either
 
explicitly or implicitly at the end of a [[../../reference/compound_stmts#with|<code>with</code>]] statement).</p>
 
<p>For example, a group of files can be opened as an &quot;all or nothing&quot;
 
operation as follows:</p>
 
 
<div class="highlight-python3 notranslate">
 
<div class="highlight-python3 notranslate">
  
 
<div class="highlight">
 
<div class="highlight">
  
<pre>with ExitStack() as stack:
+
<syntaxhighlight lang="python3">with ExitStack() as stack:
 
     files = [stack.enter_context(open(fname)) for fname in filenames]
 
     files = [stack.enter_context(open(fname)) for fname in filenames]
 
     # Hold onto the close method, but don't call it yet.
 
     # Hold onto the close method, but don't call it yet.
第536行: 第443行:
 
     # closed automatically. If all files are opened successfully,
 
     # closed automatically. If all files are opened successfully,
 
     # they will remain open even after the with statement ends.
 
     # they will remain open even after the with statement ends.
     # close_files() can then be invoked explicitly to close them all.</pre>
+
     # close_files() can then be invoked explicitly to close them all.</syntaxhighlight>
  
 
</div>
 
</div>
第543行: 第450行:
  
 
<dl>
 
<dl>
<dt><code>close</code><span class="sig-paren">(</span><span class="sig-paren">)</span></dt>
+
<dt><span class="sig-name descname"><span class="pre">close</span></span><span class="sig-paren">(</span><span class="sig-paren">)</span></dt>
<dd><p>Immediately unwinds the callback stack, invoking callbacks in the
+
<dd><p>立即展开回调堆栈,以与注册相反的顺序调用回调。 对于注册的任何上下文管理器和退出回调,传入的参数将表明没有发生异常。</p></dd></dl>
reverse order of registration. For any context managers and exit
 
callbacks registered, the arguments passed in will indicate that no
 
exception occurred.</p></dd></dl>
 
 
</dd></dl>
 
</dd></dl>
  
 
<dl>
 
<dl>
<dt>''class'' <code>contextlib.</code><code>AsyncExitStack</code></dt>
+
<dt>''<span class="pre">class</span>'' <span class="sig-prename descclassname"><span class="pre">contextlib.</span></span><span class="sig-name descname"><span class="pre">AsyncExitStack</span></span></dt>
<dd><p>An [[../../reference/datamodel#async-context-managers|<span class="std std-ref">asynchronous context manager</span>]], similar
+
<dd><p>一个 [[../../reference/datamodel#async-context-managers|异步上下文管理器]] ,类似于 [[#contextlib.ExitStack|ExitStack]],它支持结合同步和异步上下文管理器,以及具有用于清理逻辑的协程。</p>
to [[#contextlib.ExitStack|<code>ExitStack</code>]], that supports combining both synchronous and
+
<p><code>close()</code> 方法未实现,必须使用 <code>aclose()</code> 代替。</p>
asynchronous context managers, as well as having coroutines for
 
cleanup logic.</p>
 
<p>The <code>close()</code> method is not implemented, [[#contextlib.AsyncExitStack.aclose|<code>aclose()</code>]] must be used
 
instead.</p>
 
<dl>
 
<dt><code>enter_async_context</code><span class="sig-paren">(</span>''<span class="n">cm</span>''<span class="sig-paren">)</span></dt>
 
<dd><p>Similar to <code>enter_context()</code> but expects an asynchronous context
 
manager.</p></dd></dl>
 
 
 
<dl>
 
<dt><code>push_async_exit</code><span class="sig-paren">(</span>''<span class="n">exit</span>''<span class="sig-paren">)</span></dt>
 
<dd><p>Similar to <code>push()</code> but expects either an asynchronous context manager
 
or a coroutine function.</p></dd></dl>
 
 
 
 
<dl>
 
<dl>
<dt><code>push_async_callback</code><span class="sig-paren">(</span>''<span class="n">callback</span>'', ''<span class="o">/</span>'', ''<span class="o">*</span><span class="n">args</span>'', ''<span class="o">**</span><span class="n">kwds</span>''<span class="sig-paren">)</span></dt>
+
<dt><span class="sig-name descname"><span class="pre">push_async_exit</span></span><span class="sig-paren">(</span>''<span class="n"><span class="pre">exit</span></span>''<span class="sig-paren">)</span></dt>
<dd><p>Similar to <code>callback()</code> but expects a coroutine function.</p></dd></dl>
+
<dd><p><code>push()</code> 类似,但需要异步上下文管理器或协程函数。</p></dd></dl>
  
 
<dl>
 
<dl>
<dt><code>aclose</code><span class="sig-paren">(</span><span class="sig-paren">)</span></dt>
+
<dt><span class="sig-name descname"><span class="pre">push_async_callback</span></span><span class="sig-paren">(</span>''<span class="n"><span class="pre">callback</span></span>'', ''<span class="o"><span class="pre">/</span></span>'', ''<span class="o"><span class="pre">*</span></span><span class="n"><span class="pre">args</span></span>'', ''<span class="o"><span class="pre">**</span></span><span class="n"><span class="pre">kwds</span></span>''<span class="sig-paren">)</span></dt>
<dd><p>Similar to <code>close()</code> but properly handles awaitables.</p></dd></dl>
+
<dd><p>类似于 <code>callback()</code> 但需要一个协程函数。</p></dd></dl>
  
<p>Continuing the example for [[#contextlib.asynccontextmanager|<code>asynccontextmanager()</code>]]:</p>
+
<p>继续 [[#contextlib.asynccontextmanager|asynccontextmanager()]] 的示例:</p>
 
<div class="highlight-python3 notranslate">
 
<div class="highlight-python3 notranslate">
  
 
<div class="highlight">
 
<div class="highlight">
  
<pre>async with AsyncExitStack() as stack:
+
<syntaxhighlight lang="python3">async with AsyncExitStack() as stack:
 
     connections = [await stack.enter_async_context(get_connection())
 
     connections = [await stack.enter_async_context(get_connection())
 
         for i in range(5)]
 
         for i in range(5)]
 
     # All opened connections will automatically be released at the end of
 
     # All opened connections will automatically be released at the end of
 
     # the async with statement, even if attempts to open a connection
 
     # the async with statement, even if attempts to open a connection
     # later in the list raise an exception.</pre>
+
     # later in the list raise an exception.</syntaxhighlight>
  
 
</div>
 
</div>
第593行: 第483行:
 
<div class="versionadded">
 
<div class="versionadded">
  
<p><span class="versionmodified added">3.7 新版功能.</span></p>
+
<p><span class="versionmodified added">3.7 版中的新功能。</span></p>
  
 
</div></dd></dl>
 
</div></dd></dl>
第601行: 第491行:
 
<div id="examples-and-recipes" class="section">
 
<div id="examples-and-recipes" class="section">
  
== Examples and Recipes ==
+
== 例子和食谱 ==
  
This section describes some examples and recipes for making effective use of
+
本节描述了一些示例和秘诀,以有效使用 [[#module-contextlib|contextlib]] 提供的工具。
the tools provided by [[#module-contextlib|<code>contextlib</code>]].
 
  
 
<div id="supporting-a-variable-number-of-context-managers" class="section">
 
<div id="supporting-a-variable-number-of-context-managers" class="section">
  
=== Supporting a variable number of context managers ===
+
=== 支持可变数量的上下文管理器 ===
  
The primary use case for [[#contextlib.ExitStack|<code>ExitStack</code>]] is the one given in the class
+
[[#contextlib.ExitStack|ExitStack]] 的主要用例是类文档中给出的用例:在单个 [[../../reference/compound_stmts#with|with]] 语句中支持可变数量的上下文管理器和其他清理操作。 可变性可能来自由用户输入驱动的上下文管理器的数量(例如打开用户指定的文件集合),或者来自一些可选的上下文管理器:
documentation: supporting a variable number of context managers and other
 
cleanup operations in a single [[../../reference/compound_stmts#with|<code>with</code>]] statement. 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:
 
  
 
<div class="highlight-python3 notranslate">
 
<div class="highlight-python3 notranslate">
第621行: 第505行:
 
<div class="highlight">
 
<div class="highlight">
  
<pre>with ExitStack() as stack:
+
<syntaxhighlight lang="python3">with ExitStack() as stack:
 
     for resource in resources:
 
     for resource in resources:
 
         stack.enter_context(resource)
 
         stack.enter_context(resource)
第627行: 第511行:
 
         special = acquire_special_resource()
 
         special = acquire_special_resource()
 
         stack.callback(release_special_resource, special)
 
         stack.callback(release_special_resource, special)
     # Perform operations that use the acquired resources</pre>
+
     # Perform operations that use the acquired resources</syntaxhighlight>
  
 
</div>
 
</div>
  
 
</div>
 
</div>
As shown, [[#contextlib.ExitStack|<code>ExitStack</code>]] also makes it quite easy to use [[../../reference/compound_stmts#with|<code>with</code>]]
+
如图所示,[[#contextlib.ExitStack|ExitStack]] 还可以很容易地使用 [[../../reference/compound_stmts#with|]] 语句来管理本机不支持上下文管理协议的任意资源。
statements to manage arbitrary resources that don't natively support the
 
context management protocol.
 
  
  
第640行: 第522行:
 
<div id="catching-exceptions-from-enter-methods" class="section">
 
<div id="catching-exceptions-from-enter-methods" class="section">
  
=== Catching exceptions from <code>__enter__</code> methods ===
+
=== __enter__ 方法捕获异常 ===
  
It is occasionally desirable to catch exceptions from an <code>__enter__</code>
+
偶尔需要从 <code>__enter__</code> 方法实现中捕获异常,''without'' 无意中从 [[../../reference/compound_stmts#with|with]] 语句主体或上下文管理器的 <code>__exit__</code> 方法捕获异常. 通过使用 [[#contextlib.ExitStack|ExitStack]],上下文管理协议中的步骤可以稍微分开,以允许这样做:
method implementation, ''without'' inadvertently catching exceptions from
 
the [[../../reference/compound_stmts#with|<code>with</code>]] statement body or the context manager's <code>__exit__</code>
 
method. By using [[#contextlib.ExitStack|<code>ExitStack</code>]] the steps in the context management
 
protocol can be separated slightly in order to allow this:
 
  
 
<div class="highlight-python3 notranslate">
 
<div class="highlight-python3 notranslate">
第652行: 第530行:
 
<div class="highlight">
 
<div class="highlight">
  
<pre>stack = ExitStack()
+
<syntaxhighlight lang="python3">stack = ExitStack()
 
try:
 
try:
 
     x = stack.enter_context(cm)
 
     x = stack.enter_context(cm)
第659行: 第537行:
 
else:
 
else:
 
     with stack:
 
     with stack:
         # Handle normal case</pre>
+
         # Handle normal case</syntaxhighlight>
  
 
</div>
 
</div>
  
 
</div>
 
</div>
Actually needing to do this is likely to indicate that the underlying API
+
实际上需要这样做很可能表明底层 API 应该提供一个直接的资源管理接口用于 [[../../reference/compound_stmts#try|try]]/[[../../reference/compound_stmts#except|except]]/[[../../reference/compound_stmts#finally|finally]] 语句,但是并非所有 API 都在这方面设计得很好。 当上下文管理器是唯一提供的资源管理 API 时,[[#contextlib.ExitStack|ExitStack]] 可以更轻松地处理无法在 [[../../reference/compound_stmts#with|with]] 语句中直接处理的各种情况。
should be providing a direct resource management interface for use with
 
[[../../reference/compound_stmts#try|<code>try</code>]]/[[../../reference/compound_stmts#except|<code>except</code>]]/[[../../reference/compound_stmts#finally|<code>finally</code>]] statements, but not
 
all APIs are well designed in that regard. When a context manager is the
 
only resource management API provided, then [[#contextlib.ExitStack|<code>ExitStack</code>]] can make it
 
easier to handle various situations that can't be handled directly in a
 
[[../../reference/compound_stmts#with|<code>with</code>]] statement.
 
  
  
第676行: 第548行:
 
<div id="cleaning-up-in-an-enter-implementation" class="section">
 
<div id="cleaning-up-in-an-enter-implementation" class="section">
  
=== Cleaning up in an <code>__enter__</code> implementation ===
+
=== __enter__ 实现中进行清理 ===
  
As noted in the documentation of [[#contextlib.ExitStack.push|<code>ExitStack.push()</code>]], this
+
[[#contextlib.ExitStack.push|ExitStack.push()]] 文档中所述,如果 <code>__enter__()</code> 实现中的后续步骤失败,此方法可用于清理已分配的资源。
method can be useful in cleaning up an already allocated resource if later
 
steps in the <code>__enter__()</code> implementation fail.
 
  
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:
 
  
 
<div class="highlight-python3 notranslate">
 
<div class="highlight-python3 notranslate">
第690行: 第558行:
 
<div class="highlight">
 
<div class="highlight">
  
<pre>from contextlib import contextmanager, AbstractContextManager, ExitStack
+
<syntaxhighlight lang="python3">from contextlib import contextmanager, AbstractContextManager, ExitStack
  
 
class ResourceManager(AbstractContextManager):
 
class ResourceManager(AbstractContextManager):
第716行: 第584行:
 
         with self._cleanup_on_error():
 
         with self._cleanup_on_error():
 
             if not self.check_resource_ok(resource):
 
             if not self.check_resource_ok(resource):
                 msg = &quot;Failed validation for {!r}&quot;
+
                 msg = "Failed validation for {!r}"
 
                 raise RuntimeError(msg.format(resource))
 
                 raise RuntimeError(msg.format(resource))
 
         return resource
 
         return resource
第722行: 第590行:
 
     def __exit__(self, *exc_details):
 
     def __exit__(self, *exc_details):
 
         # We don't need to duplicate any of our resource release logic
 
         # We don't need to duplicate any of our resource release logic
         self.release_resource()</pre>
+
         self.release_resource()</syntaxhighlight>
  
 
</div>
 
</div>
第731行: 第599行:
 
<div id="replacing-any-use-of-try-finally-and-flag-variables" class="section">
 
<div id="replacing-any-use-of-try-finally-and-flag-variables" class="section">
  
=== Replacing any use of <code>try-finally</code> and flag variables ===
+
=== 替换 try-finally 和标志变量的任何使用 ===
  
A pattern you will sometimes see is a <code>try-finally</code> statement with a flag
+
您有时会看到的一种模式是带有标志变量的 <code>try-finally</code> 语句,以指示是否应执行 <code>finally</code> 子句的主体。 以其最简单的形式(不能仅通过使用 <code>except</code> 子句来处理),它看起来像这样:
variable to indicate whether or not the body of the <code>finally</code> clause should
 
be executed. In its simplest form (that can't already be handled just by
 
using an <code>except</code> clause instead), it looks something like this:
 
  
 
<div class="highlight-python3 notranslate">
 
<div class="highlight-python3 notranslate">
第742行: 第607行:
 
<div class="highlight">
 
<div class="highlight">
  
<pre>cleanup_needed = True
+
<syntaxhighlight lang="python3">cleanup_needed = True
 
try:
 
try:
 
     result = perform_operation()
 
     result = perform_operation()
第749行: 第614行:
 
finally:
 
finally:
 
     if cleanup_needed:
 
     if cleanup_needed:
         cleanup_resources()</pre>
+
         cleanup_resources()</syntaxhighlight>
  
 
</div>
 
</div>
  
 
</div>
 
</div>
As with any <code>try</code> statement based code, this can cause problems for
+
与任何基于 <code>try</code> 语句的代码一样,这可能会给开发和审查带来问题,因为设置代码和清理代码最终可能会被任意长的代码段分开。
development and review, because the setup code and the cleanup code can end
 
up being separated by arbitrarily long sections of code.
 
  
[[#contextlib.ExitStack|<code>ExitStack</code>]] makes it possible to instead register a callback for
+
[[#contextlib.ExitStack|ExitStack]] 可以在 <code>with</code> 语句的末尾注册一个回调来执行,然后决定跳过执行该回调:
execution at the end of a <code>with</code> statement, and then later decide to skip
 
executing that callback:
 
  
 
<div class="highlight-python3 notranslate">
 
<div class="highlight-python3 notranslate">
第766行: 第627行:
 
<div class="highlight">
 
<div class="highlight">
  
<pre>from contextlib import ExitStack
+
<syntaxhighlight lang="python3">from contextlib import ExitStack
  
 
with ExitStack() as stack:
 
with ExitStack() as stack:
第772行: 第633行:
 
     result = perform_operation()
 
     result = perform_operation()
 
     if result:
 
     if result:
         stack.pop_all()</pre>
+
         stack.pop_all()</syntaxhighlight>
  
 
</div>
 
</div>
  
 
</div>
 
</div>
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:
 
  
 
<div class="highlight-python3 notranslate">
 
<div class="highlight-python3 notranslate">
第787行: 第646行:
 
<div class="highlight">
 
<div class="highlight">
  
<pre>from contextlib import ExitStack
+
<syntaxhighlight lang="python3">from contextlib import ExitStack
  
 
class Callback(ExitStack):
 
class Callback(ExitStack):
 
     def __init__(self, callback, /, *args, **kwds):
 
     def __init__(self, callback, /, *args, **kwds):
         super(Callback, self).__init__()
+
         super().__init__()
 
         self.callback(callback, *args, **kwds)
 
         self.callback(callback, *args, **kwds)
  
第800行: 第659行:
 
     result = perform_operation()
 
     result = perform_operation()
 
     if result:
 
     if result:
         cb.cancel()</pre>
+
         cb.cancel()</syntaxhighlight>
  
 
</div>
 
</div>
  
 
</div>
 
</div>
If the resource cleanup isn't already neatly bundled into a standalone
+
如果资源清理还没有整齐地捆绑到一个独立的函数中,那么仍然可以使用 [[#contextlib.ExitStack.callback|ExitStack.callback()]] 的装饰器形式提前声明资源清理:
function, then it is still possible to use the decorator form of
 
[[#contextlib.ExitStack.callback|<code>ExitStack.callback()</code>]] to declare the resource cleanup in
 
advance:
 
  
 
<div class="highlight-python3 notranslate">
 
<div class="highlight-python3 notranslate">
第814行: 第670行:
 
<div class="highlight">
 
<div class="highlight">
  
<pre>from contextlib import ExitStack
+
<syntaxhighlight lang="python3">from contextlib import ExitStack
  
 
with ExitStack() as stack:
 
with ExitStack() as stack:
第822行: 第678行:
 
     result = perform_operation()
 
     result = perform_operation()
 
     if result:
 
     if result:
         stack.pop_all()</pre>
+
         stack.pop_all()</syntaxhighlight>
  
 
</div>
 
</div>
  
 
</div>
 
</div>
Due to the way the decorator protocol works, a callback function
+
由于装饰器协议的工作方式,以这种方式声明的回调函数不能接受任何参数。 相反,任何要释放的资源都必须作为闭包变量访问。
declared this way cannot take any parameters. Instead, any resources to
 
be released must be accessed as closure variables.
 
  
  
第835行: 第689行:
 
<div id="using-a-context-manager-as-a-function-decorator" class="section">
 
<div id="using-a-context-manager-as-a-function-decorator" class="section">
  
=== Using a context manager as a function decorator ===
+
=== 使用上下文管理器作为函数装饰器 ===
  
[[#contextlib.ContextDecorator|<code>ContextDecorator</code>]] makes it possible to use a context manager in
+
[[#contextlib.ContextDecorator|ContextDecorator]] 使得可以在普通的 <code>with</code> 语句和函数装饰器中使用上下文管理器。
both an ordinary <code>with</code> statement and also as a function decorator.
 
  
For example, it is sometimes useful to wrap functions or groups of statements
+
例如,有时用可以跟踪进入时间和退出时间的记录器包装函数或语句组很有用。 与其为任务编写函数装饰器和上下文管理器,不如从 [[#contextlib.ContextDecorator|ContextDecorator]] 继承,在单个定义中提供这两种功能:
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 [[#contextlib.ContextDecorator|<code>ContextDecorator</code>]] provides both capabilities in a
 
single definition:
 
  
 
<div class="highlight-python3 notranslate">
 
<div class="highlight-python3 notranslate">
第850行: 第699行:
 
<div class="highlight">
 
<div class="highlight">
  
<pre>from contextlib import ContextDecorator
+
<syntaxhighlight lang="python3">from contextlib import ContextDecorator
 
import logging
 
import logging
  
第863行: 第712行:
  
 
     def __exit__(self, exc_type, exc, exc_tb):
 
     def __exit__(self, exc_type, exc, exc_tb):
         logging.info('Exiting: %s', self.name)</pre>
+
         logging.info('Exiting: %s', self.name)</syntaxhighlight>
  
 
</div>
 
</div>
  
 
</div>
 
</div>
Instances of this class can be used as both a context manager:
+
此类的实例既可以用作上下文管理器:
  
 
<div class="highlight-python3 notranslate">
 
<div class="highlight-python3 notranslate">
第874行: 第723行:
 
<div class="highlight">
 
<div class="highlight">
  
<pre>with track_entry_and_exit('widget loader'):
+
<syntaxhighlight lang="python3">with track_entry_and_exit('widget loader'):
 
     print('Some time consuming activity goes here')
 
     print('Some time consuming activity goes here')
     load_widget()</pre>
+
     load_widget()</syntaxhighlight>
  
 
</div>
 
</div>
  
 
</div>
 
</div>
And also as a function decorator:
+
也可以作为函数装饰器:
  
 
<div class="highlight-python3 notranslate">
 
<div class="highlight-python3 notranslate">
第887行: 第736行:
 
<div class="highlight">
 
<div class="highlight">
  
<pre>@track_entry_and_exit('widget loader')
+
<syntaxhighlight lang="python3">@track_entry_and_exit('widget loader')
 
def activity():
 
def activity():
 
     print('Some time consuming activity goes here')
 
     print('Some time consuming activity goes here')
     load_widget()</pre>
+
     load_widget()</syntaxhighlight>
  
 
</div>
 
</div>
  
 
</div>
 
</div>
Note that there is one additional limitation when using context managers
+
请注意,使用上下文管理器作为函数装饰器时还有一个限制:无法访问 <code>__enter__()</code> 的返回值。 如果需要该值,则仍然需要使用显式 <code>with</code> 语句。
as function decorators: there's no way to access the return value of
 
<code>__enter__()</code>. If that value is needed, then it is still necessary to use
 
an explicit <code>with</code> statement.
 
  
 
<div class="admonition seealso">
 
<div class="admonition seealso">
  
参见
+
也可以看看
  
; <span id="index-0" class="target"></span>[https://www.python.org/dev/peps/pep-0343 '''PEP 343'''] - The &quot;with&quot; statement
+
; <span id="index-0" class="target"></span>[https://www.python.org/dev/peps/pep-0343 PEP 343] - “with”语句
: The specification, background, and examples for the Python [[../../reference/compound_stmts#with|<code>with</code>]] statement.
+
: Python [[../../reference/compound_stmts#with|with]] 语句的规范、背景和示例。
  
  
第916行: 第762行:
  
 
<span id="single-use-reusable-and-reentrant-cms"></span>
 
<span id="single-use-reusable-and-reentrant-cms"></span>
== Single use, reusable and reentrant context managers ==
+
== 一次性使用、可重用和可重入的上下文管理器 ==
  
Most context managers are written in a way that means they can only be
+
大多数上下文管理器的编写方式意味着它们只能在 [[../../reference/compound_stmts#with|with]] 语句中有效使用一次。 这些一次性使用的上下文管理器必须在每次使用时重新创建 - 尝试第二次使用它们将触发异常或以其他方式无法正常工作。
used effectively in a [[../../reference/compound_stmts#with|<code>with</code>]] statement once. 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
+
这种常见的限制意味着通常建议直接在使用它们的 [[../../reference/compound_stmts#with|with]] 语句的标头中创建上下文管理器(如上面的所有使用示例所示)。
context managers directly in the header of the [[../../reference/compound_stmts#with|<code>with</code>]] statement
 
where they are used (as shown in all of the usage examples above).
 
  
Files are an example of effectively single use context managers, since
+
文件是一个有效的单一使用上下文管理器的例子,因为第一个 [[../../reference/compound_stmts#with|with]] 语句将关闭文件,阻止使用该文件对象的任何进一步的 IO 操作。
the first [[../../reference/compound_stmts#with|<code>with</code>]] statement will close the file, preventing any
 
further IO operations using that file object.
 
  
Context managers created using [[#contextlib.contextmanager|<code>contextmanager()</code>]] are also single use
+
使用 [[#contextlib.contextmanager|contextmanager()]] 创建的上下文管理器也是一次性使用的上下文管理器,如果第二次尝试使用它们,将会抱怨底层生成器无法让步:
context managers, and will complain about the underlying generator failing
 
to yield if an attempt is made to use them a second time:
 
  
 
<div class="highlight-python3 notranslate">
 
<div class="highlight-python3 notranslate">
第940行: 第776行:
 
<div class="highlight">
 
<div class="highlight">
  
<pre>&gt;&gt;&gt; from contextlib import contextmanager
+
<syntaxhighlight lang="python3">>>> from contextlib import contextmanager
&gt;&gt;&gt; @contextmanager
+
>>> @contextmanager
 
... def singleuse():
 
... def singleuse():
...    print(&quot;Before&quot;)
+
...    print("Before")
 
...    yield
 
...    yield
...    print(&quot;After&quot;)
+
...    print("After")
 
...
 
...
&gt;&gt;&gt; cm = singleuse()
+
>>> cm = singleuse()
&gt;&gt;&gt; with cm:
+
>>> with cm:
 
...    pass
 
...    pass
 
...
 
...
 
Before
 
Before
 
After
 
After
&gt;&gt;&gt; with cm:
+
>>> with cm:
 
...    pass
 
...    pass
 
...
 
...
 
Traceback (most recent call last):
 
Traceback (most recent call last):
 
     ...
 
     ...
RuntimeError: generator didn't yield</pre>
+
RuntimeError: generator didn't yield</syntaxhighlight>
  
 
</div>
 
</div>
第966行: 第802行:
  
 
<span id="reentrant-cms"></span>
 
<span id="reentrant-cms"></span>
=== Reentrant context managers ===
+
=== 可重入上下文管理器 ===
  
More sophisticated context managers may be &quot;reentrant&quot;. These context
+
更复杂的上下文管理器可能是“可重入的”。 这些上下文管理器不仅可以在多个 [[../../reference/compound_stmts#with|with]] 语句中使用,还可以在 ''一个已经使用相同上下文管理器的 <code>with</code> 语句中使用'' 。
managers can not only be used in multiple [[../../reference/compound_stmts#with|<code>with</code>]] statements,
 
but may also be used ''inside'' a <code>with</code> statement that is already
 
using the same context manager.
 
  
[[../threading#threading|<code>threading.RLock</code>]] is an example of a reentrant context manager, as are
+
[[../threading#threading|threading.RLock]] 是可重入上下文管理器的示例,[[#contextlib.suppress|suppress()]] [[#contextlib.redirect_stdout|redirect_stdout()]] 也是如此。 这是一个非常简单的可重入使用示例:
[[#contextlib.suppress|<code>suppress()</code>]] and [[#contextlib.redirect_stdout|<code>redirect_stdout()</code>]]. Here's a very simple example of
 
reentrant use:
 
  
 
<div class="highlight-python3 notranslate">
 
<div class="highlight-python3 notranslate">
第981行: 第812行:
 
<div class="highlight">
 
<div class="highlight">
  
<pre>&gt;&gt;&gt; from contextlib import redirect_stdout
+
<syntaxhighlight lang="python3">>>> from contextlib import redirect_stdout
&gt;&gt;&gt; from io import StringIO
+
>>> from io import StringIO
&gt;&gt;&gt; stream = StringIO()
+
>>> stream = StringIO()
&gt;&gt;&gt; write_to_stream = redirect_stdout(stream)
+
>>> write_to_stream = redirect_stdout(stream)
&gt;&gt;&gt; with write_to_stream:
+
>>> with write_to_stream:
...    print(&quot;This is written to the stream rather than stdout&quot;)
+
...    print("This is written to the stream rather than stdout")
 
...    with write_to_stream:
 
...    with write_to_stream:
...        print(&quot;This is also written to the stream&quot;)
+
...        print("This is also written to the stream")
 
...
 
...
&gt;&gt;&gt; print(&quot;This is written directly to stdout&quot;)
+
>>> print("This is written directly to stdout")
 
This is written directly to stdout
 
This is written directly to stdout
&gt;&gt;&gt; print(stream.getvalue())
+
>>> print(stream.getvalue())
 
This is written to the stream rather than stdout
 
This is written to the stream rather than stdout
This is also written to the stream</pre>
+
This is also written to the stream</syntaxhighlight>
  
 
</div>
 
</div>
  
 
</div>
 
</div>
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.
+
另请注意,可重入 ''不是'' 与线程安全相同。 例如,[[#contextlib.redirect_stdout|redirect_stdout()]] 绝对不是线程安全的,因为它通过将 [[../sys#sys|sys.stdout]] 绑定到不同的流来对系统状态进行全局修改。
[[#contextlib.redirect_stdout|<code>redirect_stdout()</code>]], for example, is definitely not thread safe, as it
 
makes a global modification to the system state by binding [[../sys#sys|<code>sys.stdout</code>]]
 
to a different stream.
 
  
  
第1,013行: 第839行:
  
 
<span id="reusable-cms"></span>
 
<span id="reusable-cms"></span>
=== Reusable context managers ===
+
=== 可重用的上下文管理器 ===
  
Distinct from both single use and reentrant context managers are &quot;reusable&quot;
+
与一次性使用和可重入上下文管理器不同的是“可重用”上下文管理器(或者,完全明确地说,“可重用但不可重入”上下文管理器,因为可重入上下文管理器也是可重用的)。 这些上下文管理器支持多次使用,但如果特定上下文管理器实例已在包含 with 语句中使用,则会失败(或无法正常工作)。
context managers (or, to be completely explicit, &quot;reusable, but not
 
reentrant&quot; 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.
 
  
[[../threading#threading|<code>threading.Lock</code>]] is an example of a reusable, but not reentrant,
+
[[../threading#threading|threading.Lock]] 是一个可重用但不可重入的上下文管理器的示例(对于可重入锁,必须使用 [[../threading#threading|threading.RLock]] 代替)。
context manager (for a reentrant lock, it is necessary to use
 
[[../threading#threading|<code>threading.RLock</code>]] instead).
 
  
Another example of a reusable, but not reentrant, context manager is
+
另一个可重用但不可重入的上下文管理器的例子是 [[#contextlib.ExitStack|ExitStack]],因为它在离开任何 with 语句时调用 ''all'' 当前注册的回调,而不管这些回调是在哪里添加的:
[[#contextlib.ExitStack|<code>ExitStack</code>]], as it invokes ''all'' currently registered callbacks
 
when leaving any with statement, regardless of where those callbacks
 
were added:
 
  
 
<div class="highlight-python3 notranslate">
 
<div class="highlight-python3 notranslate">
第1,035行: 第851行:
 
<div class="highlight">
 
<div class="highlight">
  
<pre>&gt;&gt;&gt; from contextlib import ExitStack
+
<syntaxhighlight lang="python3">>>> from contextlib import ExitStack
&gt;&gt;&gt; stack = ExitStack()
+
>>> stack = ExitStack()
&gt;&gt;&gt; with stack:
+
>>> with stack:
...    stack.callback(print, &quot;Callback: from first context&quot;)
+
...    stack.callback(print, "Callback: from first context")
...    print(&quot;Leaving first context&quot;)
+
...    print("Leaving first context")
 
...
 
...
 
Leaving first context
 
Leaving first context
 
Callback: from first context
 
Callback: from first context
&gt;&gt;&gt; with stack:
+
>>> with stack:
...    stack.callback(print, &quot;Callback: from second context&quot;)
+
...    stack.callback(print, "Callback: from second context")
...    print(&quot;Leaving second context&quot;)
+
...    print("Leaving second context")
 
...
 
...
 
Leaving second context
 
Leaving second context
 
Callback: from second context
 
Callback: from second context
&gt;&gt;&gt; with stack:
+
>>> with stack:
...    stack.callback(print, &quot;Callback: from outer context&quot;)
+
...    stack.callback(print, "Callback: from outer context")
 
...    with stack:
 
...    with stack:
...        stack.callback(print, &quot;Callback: from inner context&quot;)
+
...        stack.callback(print, "Callback: from inner context")
...        print(&quot;Leaving inner context&quot;)
+
...        print("Leaving inner context")
...    print(&quot;Leaving outer context&quot;)
+
...    print("Leaving outer context")
 
...
 
...
 
Leaving inner context
 
Leaving inner context
 
Callback: from inner context
 
Callback: from inner context
 
Callback: from outer context
 
Callback: from outer context
Leaving outer context</pre>
+
Leaving outer context</syntaxhighlight>
  
 
</div>
 
</div>
  
 
</div>
 
</div>
As the output from the example shows, reusing a single stack object across
+
正如示例的输出所示,在多个 with 语句中重用单个堆栈对象可以正常工作,但尝试嵌套它们将导致堆栈在最里面的 with 语句的末尾被清除,这不太可能是理想的行为。
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.
 
  
Using separate [[#contextlib.ExitStack|<code>ExitStack</code>]] instances instead of reusing a single
+
使用单独的 [[#contextlib.ExitStack|ExitStack]] 实例而不是重用单个实例可以避免该问题:
instance avoids that problem:
 
  
 
<div class="highlight-python3 notranslate">
 
<div class="highlight-python3 notranslate">
第1,076行: 第888行:
 
<div class="highlight">
 
<div class="highlight">
  
<pre>&gt;&gt;&gt; from contextlib import ExitStack
+
<syntaxhighlight lang="python3">>>> from contextlib import ExitStack
&gt;&gt;&gt; with ExitStack() as outer_stack:
+
>>> with ExitStack() as outer_stack:
...    outer_stack.callback(print, &quot;Callback: from outer context&quot;)
+
...    outer_stack.callback(print, "Callback: from outer context")
 
...    with ExitStack() as inner_stack:
 
...    with ExitStack() as inner_stack:
...        inner_stack.callback(print, &quot;Callback: from inner context&quot;)
+
...        inner_stack.callback(print, "Callback: from inner context")
...        print(&quot;Leaving inner context&quot;)
+
...        print("Leaving inner context")
...    print(&quot;Leaving outer context&quot;)
+
...    print("Leaving outer context")
 
...
 
...
 
Leaving inner context
 
Leaving inner context
 
Callback: from inner context
 
Callback: from inner context
 
Leaving outer context
 
Leaving outer context
Callback: from outer context</pre>
+
Callback: from outer context</syntaxhighlight>
  
 
</div>
 
</div>
第1,096行: 第908行:
  
 
</div>
 
</div>
 +
 +
</div>
 +
<div class="clearer">
 +
 +
  
 
</div>
 
</div>
  
[[Category:Python 3.9 中文文档]]
+
[[Category:Python 3.9 文档]]

2021年10月31日 (日) 04:51的最新版本

contextlib — with 语句上下文的实用程序

源代码: :source:`Lib/contextlib.py`



该模块为涉及 with 语句的常见任务提供实用程序。 有关更多信息,另请参阅 上下文管理器类型带有语句上下文管理器

公用程序

提供的函数和类:

class contextlib.AbstractContextManager

实现 object.__enter__()object.__exit__() 的类的 抽象基类 。 提供了 object.__enter__() 的默认实现,它返回 selfobject.__exit__() 是一个抽象方法,默认返回 None ]。 另请参阅 上下文管理器类型 的定义。

3.6 版中的新功能。

class contextlib.AbstractAsyncContextManager

实现 object.__aenter__()object.__aexit__() 的类的 抽象基类 。 提供了 object.__aenter__() 的默认实现,它返回 selfobject.__aexit__() 是一个抽象方法,默认返回 None ]。 另请参阅 异步上下文管理器 的定义。

3.7 版中的新功能。

@contextlib.contextmanager

该函数是一个装饰器,可用于为语句上下文管理器定义工厂函数,无需创建类或将__enter__()__exit__() 方法。

虽然许多对象本身支持在 with 语句中使用,但有时需要管理的资源本身不是上下文管理器,并且不实现 contextlib.closingclose() 方法]

以下是一个抽象示例,以确保正确的资源管理:

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 exception

被装饰的函数在调用时必须返回一个 generator-iterator。 这个迭代器必须正好产生一个值,该值将绑定到 with 语句的 as 子句中的目标,如果有的话。

在生成器产生时,嵌套在 with 语句中的块被执行。 然后在退出块后恢复生成器。 如果块中发生未处理的异常,它会在生成器内在产生的点处重新引发。 因此,您可以使用 try...except...finally 语句来捕获错误(如果有),或确保进行一些清理。 如果一个异常被捕获只是为了记录它或执行某些操作(而不是完全抑制它),则生成器必须重新引发该异常。 否则,生成器上下文管理器将向 with 语句指示异常已被处理,并且将继续执行紧跟在 with 语句之后的语句。

contextmanager() 使用 ContextDecorator 因此它创建的上下文管理器可以用作装饰器以及在 with 语句中。 当用作装饰器时,每个函数调用都会隐式创建一个新的生成器实例(这允许由 contextmanager() 创建的“一次性”上下文管理器满足上下文管理器支持多次调用的要求以便用作装饰器)。

3.2 版更改: ContextDecorator 的使用。

@contextlib.asynccontextmanager

类似于 contextmanager(),但创建一个 异步上下文管理器

该函数是一个装饰器,可用于为async with语句异步上下文管理器定义工厂函数,无需创建类或将__aenter__()和[ X209X] 方法。 它必须应用于 异步发电机 功能。

一个简单的例子:

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 ...')

3.7 版中的新功能。

contextlib.closing(thing)

返回一个上下文管理器,在块完成时关闭 thing。 这基本上相当于:

from contextlib import contextmanager

@contextmanager
def closing(thing):
    try:
        yield thing
    finally:
        thing.close()

并让您编写这样的代码:

from contextlib import closing
from urllib.request import urlopen

with closing(urlopen('https://www.python.org')) as page:
    for line in page:
        print(line)

无需明确关闭 page。 即使发生错误,退出with块时也会调用page.close()

contextlib.nullcontext(enter_result=None)

返回一个从 __enter__ 返回 enter_result 的上下文管理器,否则什么都不做。 它旨在用作可选上下文管理器的替代品,例如:

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 something

使用 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 file

3.7 版中的新功能。

contextlib.suppress(*exceptions)

返回一个上下文管理器,如果它们发生在 with 语句的主体中,则抑制任何指定的异常,然后使用 with 语句结束后的第一个语句恢复执行。

与完全抑制异常的任何其他机制一样,此上下文管理器应仅用于覆盖非常特定的错误,其中已知静默继续执行程序是正确的做法。

例如:

from contextlib import suppress

with suppress(FileNotFoundError):
    os.remove('somefile.tmp')

with suppress(FileNotFoundError):
    os.remove('someotherfile.tmp')

此代码等效于:

try:
    os.remove('somefile.tmp')
except FileNotFoundError:
    pass

try:
    os.remove('someotherfile.tmp')
except FileNotFoundError:
    pass

这个上下文管理器是 可重入的

3.4 版中的新功能。

contextlib.redirect_stdout(new_target)

上下文管理器,用于临时将 sys.stdout 重定向到另一个文件或类似文件的对象。

此工具为输出硬连线到标准输出的现有函数或类增加了灵活性。

例如,help() 的输出通常被发送到 sys.stdout。 您可以通过将输出重定向到 io.StringIO 对象来捕获字符串中的输出。 替换流从 __enter__ 方法返回,因此可用作 with 语句的目标:

with redirect_stdout(io.StringIO()) as f:
    help(pow)
s = f.getvalue()

要将 help() 的输出发送到磁盘上的文件,请将输出重定向到常规文件:

with open('help.txt', 'w') as f:
    with redirect_stdout(f):
        help(pow)

help() 的输出发送到 sys.stderr

with redirect_stdout(sys.stderr):
    help(pow)

请注意,对 sys.stdout 的全局副作用意味着此上下文管理器不适合在库代码和大多数线程应用程序中使用。 它也对子流程的输出没有影响。 但是,对于许多实用程序脚本来说,它仍然是一种有用的方法。

这个上下文管理器是 可重入的

3.4 版中的新功能。

contextlib.redirect_stderr(new_target)

类似于 redirect_stdout() 但将 sys.stderr 重定向到另一个文件或类似文件的对象。

这个上下文管理器是 可重入的

3.5 版中的新功能。

class contextlib.ContextDecorator

使上下文管理器也可以用作装饰器的基类。

继承自 ContextDecorator 的上下文管理器必须照常实现 __enter____exit____exit__ 即使用作装饰器也保留其可选的异常处理。

ContextDecoratorcontextmanager() 使用,因此您会自动获得此功能。

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
Finishing

此更改只是以下形式的任何构造的语法糖:

def f():
    with cm():
        # Do stuff

ContextDecorator 让你改写:

@cm()
def f():
    # Do stuff

它清楚地表明 cm 适用于整个函数,而不仅仅是它的一部分(并且保存缩进级别也很好)。

已经有基类的现有上下文管理器可以通过使用 ContextDecorator 作为混合类来扩展:

from contextlib import ContextDecorator

class mycontext(ContextBaseClass, ContextDecorator):
    def __enter__(self):
        return self

    def __exit__(self, *exc):
        return False

笔记

由于修饰函数必须能够被多次调用,底层上下文管理器必须支持在多个 with 语句中使用。 如果不是这种情况,则应使用在函数内具有显式 with 语句的原始构造。

3.2 版中的新功能。

class contextlib.ExitStack

一个上下文管理器,旨在使以编程方式组合其他上下文管理器和清理功能变得容易,尤其是那些可选的或由输入数据驱动的。

例如,可以在单个 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 exception

每个实例都维护一个已注册的回调堆栈,当实例关闭时(在 with 语句的末尾显式或隐式地)调用这些回调的堆栈。 请注意,当上下文堆栈实例被垃圾收集时,回调不是 而不是 隐式调用。

使用此堆栈模型,以便可以正确处理在其 __init__ 方法(例如文件对象)中获取资源的上下文管理器。

由于注册的回调是以注册的相反顺序调用的,这最终表现为多个嵌套的 with 语句已与注册的回调集一起使用。 这甚至扩展到异常处理 - 如果内部回调抑制或替换异常,则外部回调将根据更新的状态传递参数。

这是一个相对低级的 API,负责处理正确展开退出回调堆栈的细节。 它为更高级别的上下文管理器提供了合适的基础,这些管理器以特定于应用程序的方式操作出口堆栈。

3.3 版中的新功能。

enter_context(cm)

进入一个新的上下文管理器并将其 __exit__() 方法添加到回调堆栈中。 返回值是上下文管理器自己的 __enter__() 方法的结果。

这些上下文管理器可以抑制异常,就像它们通常直接用作 with 语句的一部分一样。

push(exit)

将上下文管理器的 __exit__() 方法添加到回调堆栈。

由于 __enter__ 不是 而不是 调用,因此该方法可用于使用上下文管理器自己的 __exit__() 方法覆盖 __enter__() 实现的一部分。

如果传递的对象不是上下文管理器,则此方法假定它是与上下文管理器的 __exit__() 方法具有相同签名的回调,并将其直接添加到回调堆栈中。

通过返回真值,这些回调可以像上下文管理器 __exit__() 方法一样抑制异常。

传入的对象是从函数返回的,允许将此方法用作函数装饰器。

callback(callback, /, *args, **kwds)

接受任意回调函数和参数并将其添加到回调堆栈中。

与其他方法不同,以这种方式添加的回调无法抑制异常(因为它们永远不会传递异常详细信息)。

传入的回调是从函数返回的,允许将此方法用作函数装饰器。

pop_all()

将回调堆栈传输到一个新的 ExitStack 实例并返回它。 此操作不会调用回调 - 相反,现在将在关闭新堆栈时调用它们(在 with 语句结束时显式或隐式)。

例如,一组文件可以作为“全有或全无”操作打开,如下所示:

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()

立即展开回调堆栈,以与注册相反的顺序调用回调。 对于注册的任何上下文管理器和退出回调,传入的参数将表明没有发生异常。

class contextlib.AsyncExitStack

一个 异步上下文管理器 ,类似于 ExitStack,它支持结合同步和异步上下文管理器,以及具有用于清理逻辑的协程。

close() 方法未实现,必须使用 aclose() 代替。

push_async_exit(exit)

push() 类似,但需要异步上下文管理器或协程函数。

push_async_callback(callback, /, *args, **kwds)

类似于 callback() 但需要一个协程函数。

继续 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.

3.7 版中的新功能。


例子和食谱

本节描述了一些示例和秘诀,以有效使用 contextlib 提供的工具。

支持可变数量的上下文管理器

ExitStack 的主要用例是类文档中给出的用例:在单个 with 语句中支持可变数量的上下文管理器和其他清理操作。 可变性可能来自由用户输入驱动的上下文管理器的数量(例如打开用户指定的文件集合),或者来自一些可选的上下文管理器:

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

如图所示,ExitStack 还可以很容易地使用 语句来管理本机不支持上下文管理协议的任意资源。


从 __enter__ 方法捕获异常

偶尔需要从 __enter__ 方法实现中捕获异常,without 无意中从 with 语句主体或上下文管理器的 __exit__ 方法捕获异常. 通过使用 ExitStack,上下文管理协议中的步骤可以稍微分开,以允许这样做:

stack = ExitStack()
try:
    x = stack.enter_context(cm)
except Exception:
    # handle __enter__ exception
else:
    with stack:
        # Handle normal case

实际上需要这样做很可能表明底层 API 应该提供一个直接的资源管理接口用于 try/except/finally 语句,但是并非所有 API 都在这方面设计得很好。 当上下文管理器是唯一提供的资源管理 API 时,ExitStack 可以更轻松地处理无法在 with 语句中直接处理的各种情况。


在 __enter__ 实现中进行清理

ExitStack.push() 文档中所述,如果 __enter__() 实现中的后续步骤失败,此方法可用于清理已分配的资源。

下面是一个为上下文管理器执行此操作的示例,该上下文管理器接受资源获取和释放函数以及可选的验证函数,并将它们映射到上下文管理协议:

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()

替换 try-finally 和标志变量的任何使用

您有时会看到的一种模式是带有标志变量的 try-finally 语句,以指示是否应执行 finally 子句的主体。 以其最简单的形式(不能仅通过使用 except 子句来处理),它看起来像这样:

cleanup_needed = True
try:
    result = perform_operation()
    if result:
        cleanup_needed = False
finally:
    if cleanup_needed:
        cleanup_resources()

与任何基于 try 语句的代码一样,这可能会给开发和审查带来问题,因为设置代码和清理代码最终可能会被任意长的代码段分开。

ExitStack 可以在 with 语句的末尾注册一个回调来执行,然后决定跳过执行该回调:

from contextlib import ExitStack

with ExitStack() as stack:
    stack.callback(cleanup_resources)
    result = perform_operation()
    if result:
        stack.pop_all()

这允许预先明确预期的清理行为,而不需要单独的标志变量。

如果一个特定的应用程序经常使用这个模式,它可以通过一个小的帮助类进一步简化:

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()

如果资源清理还没有整齐地捆绑到一个独立的函数中,那么仍然可以使用 ExitStack.callback() 的装饰器形式提前声明资源清理:

from contextlib import ExitStack

with ExitStack() as stack:
    @stack.callback
    def cleanup_resources():
        ...
    result = perform_operation()
    if result:
        stack.pop_all()

由于装饰器协议的工作方式,以这种方式声明的回调函数不能接受任何参数。 相反,任何要释放的资源都必须作为闭包变量访问。


使用上下文管理器作为函数装饰器

ContextDecorator 使得可以在普通的 with 语句和函数装饰器中使用上下文管理器。

例如,有时用可以跟踪进入时间和退出时间的记录器包装函数或语句组很有用。 与其为任务编写函数装饰器和上下文管理器,不如从 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)

此类的实例既可以用作上下文管理器:

with track_entry_and_exit('widget loader'):
    print('Some time consuming activity goes here')
    load_widget()

也可以作为函数装饰器:

@track_entry_and_exit('widget loader')
def activity():
    print('Some time consuming activity goes here')
    load_widget()

请注意,使用上下文管理器作为函数装饰器时还有一个限制:无法访问 __enter__() 的返回值。 如果需要该值,则仍然需要使用显式 with 语句。

也可以看看

PEP 343 - “with”语句
Python with 语句的规范、背景和示例。


一次性使用、可重用和可重入的上下文管理器

大多数上下文管理器的编写方式意味着它们只能在 with 语句中有效使用一次。 这些一次性使用的上下文管理器必须在每次使用时重新创建 - 尝试第二次使用它们将触发异常或以其他方式无法正常工作。

这种常见的限制意味着通常建议直接在使用它们的 with 语句的标头中创建上下文管理器(如上面的所有使用示例所示)。

文件是一个有效的单一使用上下文管理器的例子,因为第一个 with 语句将关闭文件,阻止使用该文件对象的任何进一步的 IO 操作。

使用 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

可重入上下文管理器

更复杂的上下文管理器可能是“可重入的”。 这些上下文管理器不仅可以在多个 with 语句中使用,还可以在 一个已经使用相同上下文管理器的 with 语句中使用

threading.RLock 是可重入上下文管理器的示例,suppress()redirect_stdout() 也是如此。 这是一个非常简单的可重入使用示例:

>>> 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

现实世界的可重入示例更可能涉及多个相互调用的函数,因此比这个示例复杂得多。

另请注意,可重入 不是 与线程安全相同。 例如,redirect_stdout() 绝对不是线程安全的,因为它通过将 sys.stdout 绑定到不同的流来对系统状态进行全局修改。


可重用的上下文管理器

与一次性使用和可重入上下文管理器不同的是“可重用”上下文管理器(或者,完全明确地说,“可重用但不可重入”上下文管理器,因为可重入上下文管理器也是可重用的)。 这些上下文管理器支持多次使用,但如果特定上下文管理器实例已在包含 with 语句中使用,则会失败(或无法正常工作)。

threading.Lock 是一个可重用但不可重入的上下文管理器的示例(对于可重入锁,必须使用 threading.RLock 代替)。

另一个可重用但不可重入的上下文管理器的例子是 ExitStack,因为它在离开任何 with 语句时调用 all 当前注册的回调,而不管这些回调是在哪里添加的:

>>> 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

正如示例的输出所示,在多个 with 语句中重用单个堆栈对象可以正常工作,但尝试嵌套它们将导致堆栈在最里面的 with 语句的末尾被清除,这不太可能是理想的行为。

使用单独的 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