“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> | ||
− | = | + | = contextlib — with 语句上下文的实用程序 = |
− | ''' | + | '''源代码:''' [[#id1|<span id="id2" class="problematic">:source:`Lib/contextlib.py`</span>]] |
− | + | ||
− | + | ----- | |
− | [[../../reference/datamodel#context-managers| | + | |
+ | 该模块为涉及 [[../../reference/compound_stmts#with|with]] 语句的常见任务提供实用程序。 有关更多信息,另请参阅 [[../stdtypes#typecontextmanager|上下文管理器类型]] 和 [[../../reference/datamodel#context-managers|带有语句上下文管理器]] 。 | ||
<div id="utilities" class="section"> | <div id="utilities" class="section"> | ||
− | == | + | == 公用程序 == |
− | + | 提供的函数和类: | |
<dl> | <dl> | ||
− | <dt>''class'' < | + | <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> | + | <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>self</code> | ||
− | |||
<div class="versionadded"> | <div class="versionadded"> | ||
− | <p><span class="versionmodified added">3.6 | + | <p><span class="versionmodified added">3.6 版中的新功能。</span></p> |
</div></dd></dl> | </div></dd></dl> | ||
<dl> | <dl> | ||
− | <dt>''class'' < | + | <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> | + | <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>self</code> | ||
− | |||
− | [[../../reference/datamodel#async-context-managers| | ||
<div class="versionadded"> | <div class="versionadded"> | ||
− | <p><span class="versionmodified added">3.7 | + | <p><span class="versionmodified added">3.7 版中的新功能。</span></p> |
</div></dd></dl> | </div></dd></dl> | ||
<dl> | <dl> | ||
− | <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> | + | <dd><p>该函数是一个[[../../glossary#term-decorator|装饰器]],可用于为[[../../reference/compound_stmts#with|和]]语句上下文管理器定义工厂函数,无需创建类或将<code>__enter__()</code>和<code>__exit__()</code> 方法。</p> |
− | + | <p>虽然许多对象本身支持在 with 语句中使用,但有时需要管理的资源本身不是上下文管理器,并且不实现 <code>contextlib.closing</code> 的 <code>close()</code> 方法]</p> | |
− | + | <p>以下是一个抽象示例,以确保正确的资源管理:</p> | |
− | <p> | ||
− | |||
− | |||
− | <p> | ||
− | |||
<div class="highlight-python3 notranslate"> | <div class="highlight-python3 notranslate"> | ||
<div class="highlight"> | <div class="highlight"> | ||
− | < | + | <syntaxhighlight lang="python3">from contextlib import contextmanager |
@contextmanager | @contextmanager | ||
第69行: | 第57行: | ||
release_resource(resource) | release_resource(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</ | + | ... # even if code in the block raises an exception</syntaxhighlight> |
</div> | </div> | ||
</div> | </div> | ||
− | <p> | + | <p>被装饰的函数在调用时必须返回一个 [[../../glossary#term-generator|generator]]-iterator。 这个迭代器必须正好产生一个值,该值将绑定到 [[../../reference/compound_stmts#with|with]] 语句的 <code>as</code> 子句中的目标,如果有的话。</p> |
− | + | <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> | |
− | + | <p>[[#contextlib.contextmanager|contextmanager()]] 使用 [[#contextlib.ContextDecorator|ContextDecorator]] 因此它创建的上下文管理器可以用作装饰器以及在 [[../../reference/compound_stmts#with|with]] 语句中。 当用作装饰器时,每个函数调用都会隐式创建一个新的生成器实例(这允许由 [[#contextlib.contextmanager|contextmanager()]] 创建的“一次性”上下文管理器满足上下文管理器支持多次调用的要求以便用作装饰器)。</p> | |
− | <p> | ||
− | |||
− | |||
− | |||
− | [[../../reference/compound_stmts#try| | ||
− | |||
− | |||
− | |||
− | |||
− | |||
− | |||
− | <p>[[#contextlib.contextmanager| | ||
− | |||
− | |||
− | |||
− | |||
− | |||
<div class="versionchanged"> | <div class="versionchanged"> | ||
− | <p><span class="versionmodified changed"> | + | <p><span class="versionmodified changed"> 3.2 版更改:</span> [[#contextlib.ContextDecorator|ContextDecorator]] 的使用。</p> |
</div></dd></dl> | </div></dd></dl> | ||
<dl> | <dl> | ||
− | <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> | + | <dd><p>类似于 [[#contextlib.contextmanager|contextmanager()]],但创建一个 [[../../reference/datamodel#async-context-managers|异步上下文管理器]] 。</p> |
− | [[../../reference/datamodel#async-context-managers| | + | <p>该函数是一个[[../../glossary#term-decorator|装饰器]],可用于为[[../../reference/compound_stmts#async-with|async with]]语句异步上下文管理器定义工厂函数,无需创建类或将<code>__aenter__()</code>和[ X209X] 方法。 它必须应用于 [[../../glossary#term-asynchronous-generator|异步发电机]] 功能。</p> |
− | <p> | + | <p>一个简单的例子:</p> |
− | |||
− | |||
− | |||
− | |||
− | <p> | ||
<div class="highlight-python3 notranslate"> | <div class="highlight-python3 notranslate"> | ||
<div class="highlight"> | <div class="highlight"> | ||
− | < | + | <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 ...')</ | + | return conn.query('SELECT ...')</syntaxhighlight> |
</div> | </div> | ||
第135行: | 第101行: | ||
<div class="versionadded"> | <div class="versionadded"> | ||
− | <p><span class="versionmodified added">3.7 | + | <p><span class="versionmodified added">3.7 版中的新功能。</span></p> |
</div></dd></dl> | </div></dd></dl> | ||
<dl> | <dl> | ||
− | <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> | + | <dd><p>返回一个上下文管理器,在块完成时关闭 ''thing''。 这基本上相当于:</p> |
− | |||
<div class="highlight-python3 notranslate"> | <div class="highlight-python3 notranslate"> | ||
<div class="highlight"> | <div class="highlight"> | ||
− | < | + | <syntaxhighlight lang="python3">from contextlib import contextmanager |
@contextmanager | @contextmanager | ||
第154行: | 第119行: | ||
yield thing | yield thing | ||
finally: | finally: | ||
− | thing.close()</ | + | thing.close()</syntaxhighlight> |
</div> | </div> | ||
</div> | </div> | ||
− | <p> | + | <p>并让您编写这样的代码:</p> |
<div class="highlight-python3 notranslate"> | <div class="highlight-python3 notranslate"> | ||
<div class="highlight"> | <div class="highlight"> | ||
− | < | + | <syntaxhighlight lang="python3">from contextlib import closing |
from urllib.request import urlopen | from urllib.request import urlopen | ||
− | with closing(urlopen(' | + | with closing(urlopen('https://www.python.org')) as page: |
for line in page: | for line in page: | ||
− | print(line)</ | + | print(line)</syntaxhighlight> |
</div> | </div> | ||
</div> | </div> | ||
− | <p> | + | <p>无需明确关闭 <code>page</code>。 即使发生错误,退出[[../../reference/compound_stmts#with|with]]块时也会调用<code>page.close()</code>。</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>< | + | <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> | + | <dd><p>返回一个从 <code>__enter__</code> 返回 ''enter_result'' 的上下文管理器,否则什么都不做。 它旨在用作可选上下文管理器的替代品,例如:</p> |
− | |||
− | |||
<div class="highlight-python3 notranslate"> | <div class="highlight-python3 notranslate"> | ||
<div class="highlight"> | <div class="highlight"> | ||
− | < | + | <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</ | + | # Do something</syntaxhighlight> |
</div> | </div> | ||
</div> | </div> | ||
− | <p> | + | <p>使用 ''enter_result'' 的示例:</p> |
<div class="highlight-python3 notranslate"> | <div class="highlight-python3 notranslate"> | ||
<div class="highlight"> | <div class="highlight"> | ||
− | < | + | <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</ | + | # Perform processing on the file</syntaxhighlight> |
</div> | </div> | ||
第221行: | 第183行: | ||
<div class="versionadded"> | <div class="versionadded"> | ||
− | <p><span class="versionmodified added">3.7 | + | <p><span class="versionmodified added">3.7 版中的新功能。</span></p> |
</div></dd></dl> | </div></dd></dl> | ||
<dl> | <dl> | ||
− | <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> | + | <dd><p>返回一个上下文管理器,如果它们发生在 <code>with</code> 语句的主体中,则抑制任何指定的异常,然后使用 <code>with</code> 语句结束后的第一个语句恢复执行。</p> |
− | + | <p>与完全抑制异常的任何其他机制一样,此上下文管理器应仅用于覆盖非常特定的错误,其中已知静默继续执行程序是正确的做法。</p> | |
− | with | + | <p>例如:</p> |
− | <p> | ||
− | |||
− | |||
− | |||
− | <p> | ||
<div class="highlight-python3 notranslate"> | <div class="highlight-python3 notranslate"> | ||
<div class="highlight"> | <div class="highlight"> | ||
− | < | + | <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')</ | + | os.remove('someotherfile.tmp')</syntaxhighlight> |
</div> | </div> | ||
</div> | </div> | ||
− | <p> | + | <p>此代码等效于:</p> |
<div class="highlight-python3 notranslate"> | <div class="highlight-python3 notranslate"> | ||
<div class="highlight"> | <div class="highlight"> | ||
− | < | + | <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</ | + | pass</syntaxhighlight> |
</div> | </div> | ||
</div> | </div> | ||
− | <p> | + | <p>这个上下文管理器是 [[#reentrant-cms|可重入的]] 。</p> |
<div class="versionadded"> | <div class="versionadded"> | ||
− | <p><span class="versionmodified added">3.4 | + | <p><span class="versionmodified added">3.4 版中的新功能。</span></p> |
</div></dd></dl> | </div></dd></dl> | ||
<dl> | <dl> | ||
− | <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> | + | <dd><p>上下文管理器,用于临时将 [[../sys#sys|sys.stdout]] 重定向到另一个文件或类似文件的对象。</p> |
− | + | <p>此工具为输出硬连线到标准输出的现有函数或类增加了灵活性。</p> | |
− | <p> | + | <p>例如,[[../functions#help|help()]] 的输出通常被发送到 ''sys.stdout''。 您可以通过将输出重定向到 [[../io#io|io.StringIO]] 对象来捕获字符串中的输出。 替换流从 <code>__enter__</code> 方法返回,因此可用作 [[../../reference/compound_stmts#with|with]] 语句的目标:</p> |
− | |||
− | <p> | ||
− | |||
− | [[../io#io|<code> | ||
<div class="highlight-python3 notranslate"> | <div class="highlight-python3 notranslate"> | ||
<div class="highlight"> | <div class="highlight"> | ||
− | < | + | <syntaxhighlight lang="python3">with redirect_stdout(io.StringIO()) as f: |
− | |||
help(pow) | help(pow) | ||
− | s = f.getvalue()</ | + | s = f.getvalue()</syntaxhighlight> |
</div> | </div> | ||
</div> | </div> | ||
− | <p> | + | <p>要将 [[../functions#help|help()]] 的输出发送到磁盘上的文件,请将输出重定向到常规文件:</p> |
− | |||
<div class="highlight-python3 notranslate"> | <div class="highlight-python3 notranslate"> | ||
<div class="highlight"> | <div class="highlight"> | ||
− | < | + | <syntaxhighlight lang="python3">with open('help.txt', 'w') as f: |
with redirect_stdout(f): | with redirect_stdout(f): | ||
− | help(pow)</ | + | help(pow)</syntaxhighlight> |
</div> | </div> | ||
</div> | </div> | ||
− | <p> | + | <p>将 [[../functions#help|help()]] 的输出发送到 ''sys.stderr'':</p> |
<div class="highlight-python3 notranslate"> | <div class="highlight-python3 notranslate"> | ||
<div class="highlight"> | <div class="highlight"> | ||
− | < | + | <syntaxhighlight lang="python3">with redirect_stdout(sys.stderr): |
− | help(pow)</ | + | help(pow)</syntaxhighlight> |
</div> | </div> | ||
</div> | </div> | ||
− | <p> | + | <p>请注意,对 [[../sys#sys|sys.stdout]] 的全局副作用意味着此上下文管理器不适合在库代码和大多数线程应用程序中使用。 它也对子流程的输出没有影响。 但是,对于许多实用程序脚本来说,它仍然是一种有用的方法。</p> |
− | + | <p>这个上下文管理器是 [[#reentrant-cms|可重入的]] 。</p> | |
− | |||
− | |||
− | <p> | ||
<div class="versionadded"> | <div class="versionadded"> | ||
− | <p><span class="versionmodified added">3.4 | + | <p><span class="versionmodified added">3.4 版中的新功能。</span></p> |
</div></dd></dl> | </div></dd></dl> | ||
<dl> | <dl> | ||
− | <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> | + | <dd><p>类似于 [[#contextlib.redirect_stdout|redirect_stdout()]] 但将 [[../sys#sys|sys.stderr]] 重定向到另一个文件或类似文件的对象。</p> |
− | [[../sys#sys| | + | <p>这个上下文管理器是 [[#reentrant-cms|可重入的]] 。</p> |
− | <p> | ||
<div class="versionadded"> | <div class="versionadded"> | ||
− | <p><span class="versionmodified added">3.5 | + | <p><span class="versionmodified added">3.5 版中的新功能。</span></p> |
</div></dd></dl> | </div></dd></dl> | ||
<dl> | <dl> | ||
− | <dt>''class'' < | + | <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> | + | <dd><p>使上下文管理器也可以用作装饰器的基类。</p> |
− | <p> | + | <p>继承自 <code>ContextDecorator</code> 的上下文管理器必须照常实现 <code>__enter__</code> 和 <code>__exit__</code>。 <code>__exit__</code> 即使用作装饰器也保留其可选的异常处理。</p> |
− | <code>__enter__</code> | + | <p><code>ContextDecorator</code> 由 [[#contextlib.contextmanager|contextmanager()]] 使用,因此您会自动获得此功能。</p> |
− | + | <p><code>ContextDecorator</code> 示例:</p> | |
− | <p><code>ContextDecorator</code> | ||
− | |||
− | <p> | ||
<div class="highlight-python3 notranslate"> | <div class="highlight-python3 notranslate"> | ||
<div class="highlight"> | <div class="highlight"> | ||
− | < | + | <syntaxhighlight lang="python3">from contextlib import ContextDecorator |
class mycontext(ContextDecorator): | class mycontext(ContextDecorator): | ||
第366行: | 第310行: | ||
return False | return False | ||
− | + | >>> @mycontext() | |
... def function(): | ... def function(): | ||
... print('The bit in the middle') | ... print('The bit in the middle') | ||
... | ... | ||
− | + | >>> function() | |
Starting | Starting | ||
The bit in the middle | The bit in the middle | ||
Finishing | Finishing | ||
− | + | >>> 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</ | + | Finishing</syntaxhighlight> |
</div> | </div> | ||
</div> | </div> | ||
− | <p> | + | <p>此更改只是以下形式的任何构造的语法糖:</p> |
<div class="highlight-python3 notranslate"> | <div class="highlight-python3 notranslate"> | ||
<div class="highlight"> | <div class="highlight"> | ||
− | < | + | <syntaxhighlight lang="python3">def f(): |
with cm(): | with cm(): | ||
− | # Do stuff</ | + | # Do stuff</syntaxhighlight> |
</div> | </div> | ||
</div> | </div> | ||
− | <p><code>ContextDecorator</code> | + | <p><code>ContextDecorator</code> 让你改写:</p> |
<div class="highlight-python3 notranslate"> | <div class="highlight-python3 notranslate"> | ||
<div class="highlight"> | <div class="highlight"> | ||
− | < | + | <syntaxhighlight lang="python3">@cm() |
def f(): | def f(): | ||
− | # Do stuff</ | + | # Do stuff</syntaxhighlight> |
</div> | </div> | ||
</div> | </div> | ||
− | <p> | + | <p>它清楚地表明 <code>cm</code> 适用于整个函数,而不仅仅是它的一部分(并且保存缩进级别也很好)。</p> |
− | + | <p>已经有基类的现有上下文管理器可以通过使用 <code>ContextDecorator</code> 作为混合类来扩展:</p> | |
− | <p> | ||
− | |||
<div class="highlight-python3 notranslate"> | <div class="highlight-python3 notranslate"> | ||
<div class="highlight"> | <div class="highlight"> | ||
− | < | + | <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</ | + | return False</syntaxhighlight> |
</div> | </div> | ||
第431行: | 第373行: | ||
<div class="admonition note"> | <div class="admonition note"> | ||
− | <p> | + | <p>笔记</p> |
− | <p> | + | <p>由于修饰函数必须能够被多次调用,底层上下文管理器必须支持在多个 [[../../reference/compound_stmts#with|with]] 语句中使用。 如果不是这种情况,则应使用在函数内具有显式 <code>with</code> 语句的原始构造。</p> |
− | |||
− | |||
− | |||
</div> | </div> | ||
<div class="versionadded"> | <div class="versionadded"> | ||
− | <p><span class="versionmodified added">3.2 | + | <p><span class="versionmodified added">3.2 版中的新功能。</span></p> |
</div></dd></dl> | </div></dd></dl> | ||
<dl> | <dl> | ||
− | <dt>''class'' < | + | <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> | + | <dd><p>一个上下文管理器,旨在使以编程方式组合其他上下文管理器和清理功能变得容易,尤其是那些可选的或由输入数据驱动的。</p> |
− | + | <p>例如,可以在单个 with 语句中轻松处理一组文件,如下所示:</p> | |
− | |||
− | <p> | ||
− | |||
<div class="highlight-python3 notranslate"> | <div class="highlight-python3 notranslate"> | ||
<div class="highlight"> | <div class="highlight"> | ||
− | < | + | <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</ | + | # in the list raise an exception</syntaxhighlight> |
</div> | </div> | ||
</div> | </div> | ||
− | <p> | + | <p>每个实例都维护一个已注册的回调堆栈,当实例关闭时(在 [[../../reference/compound_stmts#with|with]] 语句的末尾显式或隐式地)调用这些回调的堆栈。 请注意,当上下文堆栈实例被垃圾收集时,回调不是 ''而不是'' 隐式调用。</p> |
− | + | <p>使用此堆栈模型,以便可以正确处理在其 <code>__init__</code> 方法(例如文件对象)中获取资源的上下文管理器。</p> | |
− | + | <p>由于注册的回调是以注册的相反顺序调用的,这最终表现为多个嵌套的 [[../../reference/compound_stmts#with|with]] 语句已与注册的回调集一起使用。 这甚至扩展到异常处理 - 如果内部回调抑制或替换异常,则外部回调将根据更新的状态传递参数。</p> | |
− | + | <p>这是一个相对低级的 API,负责处理正确展开退出回调堆栈的细节。 它为更高级别的上下文管理器提供了合适的基础,这些管理器以特定于应用程序的方式操作出口堆栈。</p> | |
− | <p> | ||
− | |||
− | |||
− | <p> | ||
− | |||
− | |||
− | |||
− | |||
− | |||
− | <p> | ||
− | |||
− | |||
− | |||
<div class="versionadded"> | <div class="versionadded"> | ||
− | <p><span class="versionmodified added">3.3 | + | <p><span class="versionmodified added">3.3 版中的新功能。</span></p> |
</div> | </div> | ||
<dl> | <dl> | ||
− | <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> | + | <dd><p>进入一个新的上下文管理器并将其 <code>__exit__()</code> 方法添加到回调堆栈中。 返回值是上下文管理器自己的 <code>__enter__()</code> 方法的结果。</p> |
− | + | <p>这些上下文管理器可以抑制异常,就像它们通常直接用作 [[../../reference/compound_stmts#with|with]] 语句的一部分一样。</p></dd></dl> | |
− | |||
− | <p> | ||
− | |||
<dl> | <dl> | ||
− | <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> | + | <dd><p>将上下文管理器的 <code>__exit__()</code> 方法添加到回调堆栈。</p> |
− | <p> | + | <p>由于 <code>__enter__</code> 不是 ''而不是'' 调用,因此该方法可用于使用上下文管理器自己的 <code>__exit__()</code> 方法覆盖 <code>__enter__()</code> 实现的一部分。</p> |
− | + | <p>如果传递的对象不是上下文管理器,则此方法假定它是与上下文管理器的 <code>__exit__()</code> 方法具有相同签名的回调,并将其直接添加到回调堆栈中。</p> | |
− | <code> | + | <p>通过返回真值,这些回调可以像上下文管理器 <code>__exit__()</code> 方法一样抑制异常。</p> |
− | <p> | + | <p>传入的对象是从函数返回的,允许将此方法用作函数装饰器。</p></dd></dl> |
− | |||
− | <code>__exit__()</code> | ||
− | <p> | ||
− | |||
− | <p> | ||
− | |||
<dl> | <dl> | ||
− | <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> | + | <dd><p>接受任意回调函数和参数并将其添加到回调堆栈中。</p> |
− | + | <p>与其他方法不同,以这种方式添加的回调无法抑制异常(因为它们永远不会传递异常详细信息)。</p> | |
− | <p> | + | <p>传入的回调是从函数返回的,允许将此方法用作函数装饰器。</p></dd></dl> |
− | |||
− | <p> | ||
− | |||
<dl> | <dl> | ||
− | <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> | + | <dd><p>将回调堆栈传输到一个新的 [[#contextlib.ExitStack|ExitStack]] 实例并返回它。 此操作不会调用回调 - 相反,现在将在关闭新堆栈时调用它们(在 [[../../reference/compound_stmts#with|with]] 语句结束时显式或隐式)。</p> |
− | + | <p>例如,一组文件可以作为“全有或全无”操作打开,如下所示:</p> | |
− | |||
− | |||
− | <p> | ||
− | |||
<div class="highlight-python3 notranslate"> | <div class="highlight-python3 notranslate"> | ||
<div class="highlight"> | <div class="highlight"> | ||
− | < | + | <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.</ | + | # close_files() can then be invoked explicitly to close them all.</syntaxhighlight> |
</div> | </div> | ||
第543行: | 第450行: | ||
<dl> | <dl> | ||
− | <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> | + | <dd><p>立即展开回调堆栈,以与注册相反的顺序调用回调。 对于注册的任何上下文管理器和退出回调,传入的参数将表明没有发生异常。</p></dd></dl> |
− | |||
− | |||
− | |||
</dd></dl> | </dd></dl> | ||
<dl> | <dl> | ||
− | <dt>''class | + | <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>一个 [[../../reference/datamodel#async-context-managers|异步上下文管理器]] ,类似于 [[#contextlib.ExitStack|ExitStack]],它支持结合同步和异步上下文管理器,以及具有用于清理逻辑的协程。</p> | |
− | + | <p><code>close()</code> 方法未实现,必须使用 <code>aclose()</code> 代替。</p> | |
− | |||
− | |||
− | |||
− | |||
− | |||
− | |||
− | <dd><p> | ||
− | |||
− | |||
− | < | ||
− | |||
− | |||
− | |||
− | |||
<dl> | <dl> | ||
− | <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> | + | <dd><p>与 <code>push()</code> 类似,但需要异步上下文管理器或协程函数。</p></dd></dl> |
<dl> | <dl> | ||
− | <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> | + | <dd><p>类似于 <code>callback()</code> 但需要一个协程函数。</p></dd></dl> |
− | <p> | + | <p>继续 [[#contextlib.asynccontextmanager|asynccontextmanager()]] 的示例:</p> |
<div class="highlight-python3 notranslate"> | <div class="highlight-python3 notranslate"> | ||
<div class="highlight"> | <div class="highlight"> | ||
− | < | + | <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.</ | + | # 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 | + | <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"> | ||
− | == | + | == 例子和食谱 == |
− | + | 本节描述了一些示例和秘诀,以有效使用 [[#module-contextlib|contextlib]] 提供的工具。 | |
− | |||
<div id="supporting-a-variable-number-of-context-managers" class="section"> | <div id="supporting-a-variable-number-of-context-managers" class="section"> | ||
− | === | + | === 支持可变数量的上下文管理器 === |
− | + | [[#contextlib.ExitStack|ExitStack]] 的主要用例是类文档中给出的用例:在单个 [[../../reference/compound_stmts#with|with]] 语句中支持可变数量的上下文管理器和其他清理操作。 可变性可能来自由用户输入驱动的上下文管理器的数量(例如打开用户指定的文件集合),或者来自一些可选的上下文管理器: | |
− | |||
− | |||
− | |||
− | |||
− | |||
<div class="highlight-python3 notranslate"> | <div class="highlight-python3 notranslate"> | ||
第621行: | 第505行: | ||
<div class="highlight"> | <div class="highlight"> | ||
− | < | + | <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</ | + | # Perform operations that use the acquired resources</syntaxhighlight> |
</div> | </div> | ||
</div> | </div> | ||
− | + | 如图所示,[[#contextlib.ExitStack|ExitStack]] 还可以很容易地使用 [[../../reference/compound_stmts#with|和]] 语句来管理本机不支持上下文管理协议的任意资源。 | |
− | |||
− | |||
第640行: | 第522行: | ||
<div id="catching-exceptions-from-enter-methods" class="section"> | <div id="catching-exceptions-from-enter-methods" class="section"> | ||
− | === | + | === 从 __enter__ 方法捕获异常 === |
− | + | 偶尔需要从 <code>__enter__</code> 方法实现中捕获异常,''without'' 无意中从 [[../../reference/compound_stmts#with|with]] 语句主体或上下文管理器的 <code>__exit__</code> 方法捕获异常. 通过使用 [[#contextlib.ExitStack|ExitStack]],上下文管理协议中的步骤可以稍微分开,以允许这样做: | |
− | |||
− | |||
− | |||
− | |||
<div class="highlight-python3 notranslate"> | <div class="highlight-python3 notranslate"> | ||
第652行: | 第530行: | ||
<div class="highlight"> | <div class="highlight"> | ||
− | < | + | <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</ | + | # Handle normal case</syntaxhighlight> |
</div> | </div> | ||
</div> | </div> | ||
− | + | 实际上需要这样做很可能表明底层 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]] 语句中直接处理的各种情况。 | |
− | |||
− | [[../../reference/compound_stmts#try| | ||
− | |||
− | |||
− | |||
− | [[../../reference/compound_stmts#with| | ||
第676行: | 第548行: | ||
<div id="cleaning-up-in-an-enter-implementation" class="section"> | <div id="cleaning-up-in-an-enter-implementation" class="section"> | ||
− | === | + | === 在 __enter__ 实现中进行清理 === |
− | + | 如 [[#contextlib.ExitStack.push|ExitStack.push()]] 文档中所述,如果 <code>__enter__()</code> 实现中的后续步骤失败,此方法可用于清理已分配的资源。 | |
− | |||
− | |||
− | + | 下面是一个为上下文管理器执行此操作的示例,该上下文管理器接受资源获取和释放函数以及可选的验证函数,并将它们映射到上下文管理协议: | |
− | |||
− | |||
<div class="highlight-python3 notranslate"> | <div class="highlight-python3 notranslate"> | ||
第690行: | 第558行: | ||
<div class="highlight"> | <div class="highlight"> | ||
− | < | + | <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 = | + | 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()</ | + | 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"> | ||
− | === | + | === 替换 try-finally 和标志变量的任何使用 === |
− | + | 您有时会看到的一种模式是带有标志变量的 <code>try-finally</code> 语句,以指示是否应执行 <code>finally</code> 子句的主体。 以其最简单的形式(不能仅通过使用 <code>except</code> 子句来处理),它看起来像这样: | |
− | |||
− | |||
− | |||
<div class="highlight-python3 notranslate"> | <div class="highlight-python3 notranslate"> | ||
第742行: | 第607行: | ||
<div class="highlight"> | <div class="highlight"> | ||
− | < | + | <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()</ | + | cleanup_resources()</syntaxhighlight> |
</div> | </div> | ||
</div> | </div> | ||
− | + | 与任何基于 <code>try</code> 语句的代码一样,这可能会给开发和审查带来问题,因为设置代码和清理代码最终可能会被任意长的代码段分开。 | |
− | |||
− | |||
− | [[#contextlib.ExitStack| | + | [[#contextlib.ExitStack|ExitStack]] 可以在 <code>with</code> 语句的末尾注册一个回调来执行,然后决定跳过执行该回调: |
− | |||
− | |||
<div class="highlight-python3 notranslate"> | <div class="highlight-python3 notranslate"> | ||
第766行: | 第627行: | ||
<div class="highlight"> | <div class="highlight"> | ||
− | < | + | <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()</ | + | stack.pop_all()</syntaxhighlight> |
</div> | </div> | ||
</div> | </div> | ||
− | + | 这允许预先明确预期的清理行为,而不需要单独的标志变量。 | |
− | |||
− | + | 如果一个特定的应用程序经常使用这个模式,它可以通过一个小的帮助类进一步简化: | |
− | |||
<div class="highlight-python3 notranslate"> | <div class="highlight-python3 notranslate"> | ||
第787行: | 第646行: | ||
<div class="highlight"> | <div class="highlight"> | ||
− | < | + | <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( | + | 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()</ | + | cb.cancel()</syntaxhighlight> |
</div> | </div> | ||
</div> | </div> | ||
− | + | 如果资源清理还没有整齐地捆绑到一个独立的函数中,那么仍然可以使用 [[#contextlib.ExitStack.callback|ExitStack.callback()]] 的装饰器形式提前声明资源清理: | |
− | |||
− | [[#contextlib.ExitStack.callback| | ||
− | |||
<div class="highlight-python3 notranslate"> | <div class="highlight-python3 notranslate"> | ||
第814行: | 第670行: | ||
<div class="highlight"> | <div class="highlight"> | ||
− | < | + | <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()</ | + | stack.pop_all()</syntaxhighlight> |
</div> | </div> | ||
</div> | </div> | ||
− | + | 由于装饰器协议的工作方式,以这种方式声明的回调函数不能接受任何参数。 相反,任何要释放的资源都必须作为闭包变量访问。 | |
− | |||
− | |||
第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"> | ||
− | === | + | === 使用上下文管理器作为函数装饰器 === |
− | [[#contextlib.ContextDecorator| | + | [[#contextlib.ContextDecorator|ContextDecorator]] 使得可以在普通的 <code>with</code> 语句和函数装饰器中使用上下文管理器。 |
− | |||
− | + | 例如,有时用可以跟踪进入时间和退出时间的记录器包装函数或语句组很有用。 与其为任务编写函数装饰器和上下文管理器,不如从 [[#contextlib.ContextDecorator|ContextDecorator]] 继承,在单个定义中提供这两种功能: | |
− | |||
− | |||
− | |||
− | |||
<div class="highlight-python3 notranslate"> | <div class="highlight-python3 notranslate"> | ||
第850行: | 第699行: | ||
<div class="highlight"> | <div class="highlight"> | ||
− | < | + | <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)</ | + | logging.info('Exiting: %s', self.name)</syntaxhighlight> |
</div> | </div> | ||
</div> | </div> | ||
− | + | 此类的实例既可以用作上下文管理器: | |
<div class="highlight-python3 notranslate"> | <div class="highlight-python3 notranslate"> | ||
第874行: | 第723行: | ||
<div class="highlight"> | <div class="highlight"> | ||
− | < | + | <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()</ | + | load_widget()</syntaxhighlight> |
</div> | </div> | ||
</div> | </div> | ||
− | + | 也可以作为函数装饰器: | |
<div class="highlight-python3 notranslate"> | <div class="highlight-python3 notranslate"> | ||
第887行: | 第736行: | ||
<div class="highlight"> | <div class="highlight"> | ||
− | < | + | <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()</ | + | load_widget()</syntaxhighlight> |
</div> | </div> | ||
</div> | </div> | ||
− | + | 请注意,使用上下文管理器作为函数装饰器时还有一个限制:无法访问 <code>__enter__()</code> 的返回值。 如果需要该值,则仍然需要使用显式 <code>with</code> 语句。 | |
− | |||
− | <code>__enter__()</code> | ||
− | |||
<div class="admonition seealso"> | <div class="admonition seealso"> | ||
− | + | 也可以看看 | |
− | ; <span id="index-0" class="target"></span>[https://www.python.org/dev/peps/pep-0343 | + | ; <span id="index-0" class="target"></span>[https://www.python.org/dev/peps/pep-0343 PEP 343] - “with”语句 |
− | : | + | : 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> | ||
− | == | + | == 一次性使用、可重用和可重入的上下文管理器 == |
− | + | 大多数上下文管理器的编写方式意味着它们只能在 [[../../reference/compound_stmts#with|with]] 语句中有效使用一次。 这些一次性使用的上下文管理器必须在每次使用时重新创建 - 尝试第二次使用它们将触发异常或以其他方式无法正常工作。 | |
− | |||
− | |||
− | |||
− | |||
− | + | 这种常见的限制意味着通常建议直接在使用它们的 [[../../reference/compound_stmts#with|with]] 语句的标头中创建上下文管理器(如上面的所有使用示例所示)。 | |
− | |||
− | |||
− | + | 文件是一个有效的单一使用上下文管理器的例子,因为第一个 [[../../reference/compound_stmts#with|with]] 语句将关闭文件,阻止使用该文件对象的任何进一步的 IO 操作。 | |
− | |||
− | |||
− | + | 使用 [[#contextlib.contextmanager|contextmanager()]] 创建的上下文管理器也是一次性使用的上下文管理器,如果第二次尝试使用它们,将会抱怨底层生成器无法让步: | |
− | |||
− | |||
<div class="highlight-python3 notranslate"> | <div class="highlight-python3 notranslate"> | ||
第940行: | 第776行: | ||
<div class="highlight"> | <div class="highlight"> | ||
− | < | + | <syntaxhighlight lang="python3">>>> from contextlib import contextmanager |
− | + | >>> @contextmanager | |
... def singleuse(): | ... def singleuse(): | ||
− | ... print( | + | ... print("Before") |
... yield | ... yield | ||
− | ... print( | + | ... print("After") |
... | ... | ||
− | + | >>> cm = singleuse() | |
− | + | >>> with cm: | |
... pass | ... pass | ||
... | ... | ||
Before | Before | ||
After | After | ||
− | + | >>> with cm: | |
... pass | ... pass | ||
... | ... | ||
Traceback (most recent call last): | Traceback (most recent call last): | ||
... | ... | ||
− | RuntimeError: generator didn't yield</ | + | RuntimeError: generator didn't yield</syntaxhighlight> |
</div> | </div> | ||
第966行: | 第802行: | ||
<span id="reentrant-cms"></span> | <span id="reentrant-cms"></span> | ||
− | === | + | === 可重入上下文管理器 === |
− | + | 更复杂的上下文管理器可能是“可重入的”。 这些上下文管理器不仅可以在多个 [[../../reference/compound_stmts#with|with]] 语句中使用,还可以在 ''一个已经使用相同上下文管理器的 <code>with</code> 语句中使用'' 。 | |
− | |||
− | |||
− | |||
− | [[../threading#threading| | + | [[../threading#threading|threading.RLock]] 是可重入上下文管理器的示例,[[#contextlib.suppress|suppress()]] 和 [[#contextlib.redirect_stdout|redirect_stdout()]] 也是如此。 这是一个非常简单的可重入使用示例: |
− | [[#contextlib.suppress| | ||
− | |||
<div class="highlight-python3 notranslate"> | <div class="highlight-python3 notranslate"> | ||
第981行: | 第812行: | ||
<div class="highlight"> | <div class="highlight"> | ||
− | < | + | <syntaxhighlight lang="python3">>>> from contextlib import redirect_stdout |
− | + | >>> from io import StringIO | |
− | + | >>> stream = StringIO() | |
− | + | >>> write_to_stream = redirect_stdout(stream) | |
− | + | >>> with write_to_stream: | |
− | ... print( | + | ... print("This is written to the stream rather than stdout") |
... with write_to_stream: | ... with write_to_stream: | ||
− | ... print( | + | ... print("This is also written to the stream") |
... | ... | ||
− | + | >>> print("This is written directly to stdout") | |
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 written to the stream rather than stdout | ||
− | This is also written to the stream</ | + | This is also written to the stream</syntaxhighlight> |
</div> | </div> | ||
</div> | </div> | ||
− | + | 现实世界的可重入示例更可能涉及多个相互调用的函数,因此比这个示例复杂得多。 | |
− | |||
− | |||
− | + | 另请注意,可重入 ''不是'' 与线程安全相同。 例如,[[#contextlib.redirect_stdout|redirect_stdout()]] 绝对不是线程安全的,因为它通过将 [[../sys#sys|sys.stdout]] 绑定到不同的流来对系统状态进行全局修改。 | |
− | [[#contextlib.redirect_stdout| | ||
− | |||
− | |||
第1,013行: | 第839行: | ||
<span id="reusable-cms"></span> | <span id="reusable-cms"></span> | ||
− | === | + | === 可重用的上下文管理器 === |
− | + | 与一次性使用和可重入上下文管理器不同的是“可重用”上下文管理器(或者,完全明确地说,“可重用但不可重入”上下文管理器,因为可重入上下文管理器也是可重用的)。 这些上下文管理器支持多次使用,但如果特定上下文管理器实例已在包含 with 语句中使用,则会失败(或无法正常工作)。 | |
− | |||
− | |||
− | |||
− | |||
− | |||
− | [[../threading#threading| | + | [[../threading#threading|threading.Lock]] 是一个可重用但不可重入的上下文管理器的示例(对于可重入锁,必须使用 [[../threading#threading|threading.RLock]] 代替)。 |
− | |||
− | [[../threading#threading| | ||
− | + | 另一个可重用但不可重入的上下文管理器的例子是 [[#contextlib.ExitStack|ExitStack]],因为它在离开任何 with 语句时调用 ''all'' 当前注册的回调,而不管这些回调是在哪里添加的: | |
− | [[#contextlib.ExitStack| | ||
− | |||
− | |||
<div class="highlight-python3 notranslate"> | <div class="highlight-python3 notranslate"> | ||
第1,035行: | 第851行: | ||
<div class="highlight"> | <div class="highlight"> | ||
− | < | + | <syntaxhighlight lang="python3">>>> from contextlib import ExitStack |
− | + | >>> stack = ExitStack() | |
− | + | >>> with stack: | |
− | ... stack.callback(print, | + | ... stack.callback(print, "Callback: from first context") |
− | ... print( | + | ... print("Leaving first context") |
... | ... | ||
Leaving first context | Leaving first context | ||
Callback: from first context | Callback: from first context | ||
− | + | >>> with stack: | |
− | ... stack.callback(print, | + | ... stack.callback(print, "Callback: from second context") |
− | ... print( | + | ... print("Leaving second context") |
... | ... | ||
Leaving second context | Leaving second context | ||
Callback: from second context | Callback: from second context | ||
− | + | >>> with stack: | |
− | ... stack.callback(print, | + | ... stack.callback(print, "Callback: from outer context") |
... with stack: | ... with stack: | ||
− | ... stack.callback(print, | + | ... stack.callback(print, "Callback: from inner context") |
− | ... print( | + | ... print("Leaving inner context") |
− | ... print( | + | ... 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</ | + | Leaving outer context</syntaxhighlight> |
</div> | </div> | ||
</div> | </div> | ||
− | + | 正如示例的输出所示,在多个 with 语句中重用单个堆栈对象可以正常工作,但尝试嵌套它们将导致堆栈在最里面的 with 语句的末尾被清除,这不太可能是理想的行为。 | |
− | |||
− | |||
− | |||
− | + | 使用单独的 [[#contextlib.ExitStack|ExitStack]] 实例而不是重用单个实例可以避免该问题: | |
− | |||
<div class="highlight-python3 notranslate"> | <div class="highlight-python3 notranslate"> | ||
第1,076行: | 第888行: | ||
<div class="highlight"> | <div class="highlight"> | ||
− | < | + | <syntaxhighlight lang="python3">>>> from contextlib import ExitStack |
− | + | >>> with ExitStack() as outer_stack: | |
− | ... outer_stack.callback(print, | + | ... outer_stack.callback(print, "Callback: from outer context") |
... with ExitStack() as inner_stack: | ... with ExitStack() as inner_stack: | ||
− | ... inner_stack.callback(print, | + | ... inner_stack.callback(print, "Callback: from inner context") |
− | ... print( | + | ... print("Leaving inner context") |
− | ... print( | + | ... 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</ | + | 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__() 的默认实现,它返回
self
而 object.__exit__() 是一个抽象方法,默认返回None
]。 另请参阅 上下文管理器类型 的定义。3.6 版中的新功能。
- class contextlib.AbstractAsyncContextManager
实现 object.__aenter__() 和 object.__aexit__() 的类的 抽象基类 。 提供了 object.__aenter__() 的默认实现,它返回
self
而 object.__aexit__() 是一个抽象方法,默认返回None
]。 另请参阅 异步上下文管理器 的定义。3.7 版中的新功能。
- @contextlib.contextmanager
该函数是一个装饰器,可用于为和语句上下文管理器定义工厂函数,无需创建类或将
__enter__()
和__exit__()
方法。虽然许多对象本身支持在 with 语句中使用,但有时需要管理的资源本身不是上下文管理器,并且不实现
contextlib.closing
的close()
方法]以下是一个抽象示例,以确保正确的资源管理:
被装饰的函数在调用时必须返回一个 generator-iterator。 这个迭代器必须正好产生一个值,该值将绑定到 with 语句的
as
子句中的目标,如果有的话。在生成器产生时,嵌套在 with 语句中的块被执行。 然后在退出块后恢复生成器。 如果块中发生未处理的异常,它会在生成器内在产生的点处重新引发。 因此,您可以使用 try...except...finally 语句来捕获错误(如果有),或确保进行一些清理。 如果一个异常被捕获只是为了记录它或执行某些操作(而不是完全抑制它),则生成器必须重新引发该异常。 否则,生成器上下文管理器将向
with
语句指示异常已被处理,并且将继续执行紧跟在with
语句之后的语句。contextmanager() 使用 ContextDecorator 因此它创建的上下文管理器可以用作装饰器以及在 with 语句中。 当用作装饰器时,每个函数调用都会隐式创建一个新的生成器实例(这允许由 contextmanager() 创建的“一次性”上下文管理器满足上下文管理器支持多次调用的要求以便用作装饰器)。
3.2 版更改: ContextDecorator 的使用。
- @contextlib.asynccontextmanager
类似于 contextmanager(),但创建一个 异步上下文管理器 。
该函数是一个装饰器,可用于为async with语句异步上下文管理器定义工厂函数,无需创建类或将
__aenter__()
和[ X209X] 方法。 它必须应用于 异步发电机 功能。一个简单的例子:
3.7 版中的新功能。
- contextlib.closing(thing)
返回一个上下文管理器,在块完成时关闭 thing。 这基本上相当于:
并让您编写这样的代码:
无需明确关闭
page
。 即使发生错误,退出with块时也会调用page.close()
。
- contextlib.nullcontext(enter_result=None)
返回一个从
__enter__
返回 enter_result 的上下文管理器,否则什么都不做。 它旨在用作可选上下文管理器的替代品,例如:使用 enter_result 的示例:
3.7 版中的新功能。
- contextlib.suppress(*exceptions)
返回一个上下文管理器,如果它们发生在
with
语句的主体中,则抑制任何指定的异常,然后使用with
语句结束后的第一个语句恢复执行。与完全抑制异常的任何其他机制一样,此上下文管理器应仅用于覆盖非常特定的错误,其中已知静默继续执行程序是正确的做法。
例如:
此代码等效于:
这个上下文管理器是 可重入的 。
3.4 版中的新功能。
- contextlib.redirect_stdout(new_target)
上下文管理器,用于临时将 sys.stdout 重定向到另一个文件或类似文件的对象。
此工具为输出硬连线到标准输出的现有函数或类增加了灵活性。
例如,help() 的输出通常被发送到 sys.stdout。 您可以通过将输出重定向到 io.StringIO 对象来捕获字符串中的输出。 替换流从
__enter__
方法返回,因此可用作 with 语句的目标:要将 help() 的输出发送到磁盘上的文件,请将输出重定向到常规文件:
将 help() 的输出发送到 sys.stderr:
请注意,对 sys.stdout 的全局副作用意味着此上下文管理器不适合在库代码和大多数线程应用程序中使用。 它也对子流程的输出没有影响。 但是,对于许多实用程序脚本来说,它仍然是一种有用的方法。
这个上下文管理器是 可重入的 。
3.4 版中的新功能。
- contextlib.redirect_stderr(new_target)
类似于 redirect_stdout() 但将 sys.stderr 重定向到另一个文件或类似文件的对象。
这个上下文管理器是 可重入的 。
3.5 版中的新功能。
- class contextlib.ContextDecorator
使上下文管理器也可以用作装饰器的基类。
继承自
ContextDecorator
的上下文管理器必须照常实现__enter__
和__exit__
。__exit__
即使用作装饰器也保留其可选的异常处理。ContextDecorator
由 contextmanager() 使用,因此您会自动获得此功能。ContextDecorator
示例:此更改只是以下形式的任何构造的语法糖:
ContextDecorator
让你改写:它清楚地表明
cm
适用于整个函数,而不仅仅是它的一部分(并且保存缩进级别也很好)。已经有基类的现有上下文管理器可以通过使用
ContextDecorator
作为混合类来扩展:笔记
由于修饰函数必须能够被多次调用,底层上下文管理器必须支持在多个 with 语句中使用。 如果不是这种情况,则应使用在函数内具有显式
with
语句的原始构造。3.2 版中的新功能。
- class contextlib.ExitStack
一个上下文管理器,旨在使以编程方式组合其他上下文管理器和清理功能变得容易,尤其是那些可选的或由输入数据驱动的。
例如,可以在单个 with 语句中轻松处理一组文件,如下所示:
每个实例都维护一个已注册的回调堆栈,当实例关闭时(在 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 语句结束时显式或隐式)。
例如,一组文件可以作为“全有或全无”操作打开,如下所示:
- close()
立即展开回调堆栈,以与注册相反的顺序调用回调。 对于注册的任何上下文管理器和退出回调,传入的参数将表明没有发生异常。
- class contextlib.AsyncExitStack
一个 异步上下文管理器 ,类似于 ExitStack,它支持结合同步和异步上下文管理器,以及具有用于清理逻辑的协程。
close()
方法未实现,必须使用aclose()
代替。- push_async_exit(exit)
与
push()
类似,但需要异步上下文管理器或协程函数。
- push_async_callback(callback, /, *args, **kwds)
类似于
callback()
但需要一个协程函数。
继续 asynccontextmanager() 的示例:
3.7 版中的新功能。
例子和食谱
本节描述了一些示例和秘诀,以有效使用 contextlib 提供的工具。
支持可变数量的上下文管理器
ExitStack 的主要用例是类文档中给出的用例:在单个 with 语句中支持可变数量的上下文管理器和其他清理操作。 可变性可能来自由用户输入驱动的上下文管理器的数量(例如打开用户指定的文件集合),或者来自一些可选的上下文管理器:
如图所示,ExitStack 还可以很容易地使用 和 语句来管理本机不支持上下文管理协议的任意资源。
从 __enter__ 方法捕获异常
偶尔需要从 __enter__
方法实现中捕获异常,without 无意中从 with 语句主体或上下文管理器的 __exit__
方法捕获异常. 通过使用 ExitStack,上下文管理协议中的步骤可以稍微分开,以允许这样做:
实际上需要这样做很可能表明底层 API 应该提供一个直接的资源管理接口用于 try/except/finally 语句,但是并非所有 API 都在这方面设计得很好。 当上下文管理器是唯一提供的资源管理 API 时,ExitStack 可以更轻松地处理无法在 with 语句中直接处理的各种情况。
在 __enter__ 实现中进行清理
如 ExitStack.push() 文档中所述,如果 __enter__()
实现中的后续步骤失败,此方法可用于清理已分配的资源。
下面是一个为上下文管理器执行此操作的示例,该上下文管理器接受资源获取和释放函数以及可选的验证函数,并将它们映射到上下文管理协议:
替换 try-finally 和标志变量的任何使用
您有时会看到的一种模式是带有标志变量的 try-finally
语句,以指示是否应执行 finally
子句的主体。 以其最简单的形式(不能仅通过使用 except
子句来处理),它看起来像这样:
与任何基于 try
语句的代码一样,这可能会给开发和审查带来问题,因为设置代码和清理代码最终可能会被任意长的代码段分开。
ExitStack 可以在 with
语句的末尾注册一个回调来执行,然后决定跳过执行该回调:
这允许预先明确预期的清理行为,而不需要单独的标志变量。
如果一个特定的应用程序经常使用这个模式,它可以通过一个小的帮助类进一步简化:
如果资源清理还没有整齐地捆绑到一个独立的函数中,那么仍然可以使用 ExitStack.callback() 的装饰器形式提前声明资源清理:
由于装饰器协议的工作方式,以这种方式声明的回调函数不能接受任何参数。 相反,任何要释放的资源都必须作为闭包变量访问。
使用上下文管理器作为函数装饰器
ContextDecorator 使得可以在普通的 with
语句和函数装饰器中使用上下文管理器。
例如,有时用可以跟踪进入时间和退出时间的记录器包装函数或语句组很有用。 与其为任务编写函数装饰器和上下文管理器,不如从 ContextDecorator 继承,在单个定义中提供这两种功能:
此类的实例既可以用作上下文管理器:
也可以作为函数装饰器:
请注意,使用上下文管理器作为函数装饰器时还有一个限制:无法访问 __enter__()
的返回值。 如果需要该值,则仍然需要使用显式 with
语句。
一次性使用、可重用和可重入的上下文管理器
大多数上下文管理器的编写方式意味着它们只能在 with 语句中有效使用一次。 这些一次性使用的上下文管理器必须在每次使用时重新创建 - 尝试第二次使用它们将触发异常或以其他方式无法正常工作。
这种常见的限制意味着通常建议直接在使用它们的 with 语句的标头中创建上下文管理器(如上面的所有使用示例所示)。
文件是一个有效的单一使用上下文管理器的例子,因为第一个 with 语句将关闭文件,阻止使用该文件对象的任何进一步的 IO 操作。
使用 contextmanager() 创建的上下文管理器也是一次性使用的上下文管理器,如果第二次尝试使用它们,将会抱怨底层生成器无法让步:
可重入上下文管理器
更复杂的上下文管理器可能是“可重入的”。 这些上下文管理器不仅可以在多个 with 语句中使用,还可以在 一个已经使用相同上下文管理器的 with
语句中使用 。
threading.RLock 是可重入上下文管理器的示例,suppress() 和 redirect_stdout() 也是如此。 这是一个非常简单的可重入使用示例:
现实世界的可重入示例更可能涉及多个相互调用的函数,因此比这个示例复杂得多。
另请注意,可重入 不是 与线程安全相同。 例如,redirect_stdout() 绝对不是线程安全的,因为它通过将 sys.stdout 绑定到不同的流来对系统状态进行全局修改。
可重用的上下文管理器
与一次性使用和可重入上下文管理器不同的是“可重用”上下文管理器(或者,完全明确地说,“可重用但不可重入”上下文管理器,因为可重入上下文管理器也是可重用的)。 这些上下文管理器支持多次使用,但如果特定上下文管理器实例已在包含 with 语句中使用,则会失败(或无法正常工作)。
threading.Lock 是一个可重用但不可重入的上下文管理器的示例(对于可重入锁,必须使用 threading.RLock 代替)。
另一个可重用但不可重入的上下文管理器的例子是 ExitStack,因为它在离开任何 with 语句时调用 all 当前注册的回调,而不管这些回调是在哪里添加的:
正如示例的输出所示,在多个 with 语句中重用单个堆栈对象可以正常工作,但尝试嵌套它们将导致堆栈在最里面的 with 语句的末尾被清除,这不太可能是理想的行为。
使用单独的 ExitStack 实例而不是重用单个实例可以避免该问题: