contextlib --- with 文コンテキスト甚ナヌティリティ¶

゜ヌスコヌド: Lib/contextlib.py


このモゞュヌルは with 文に関わる䞀般的なタスクのためのナヌティリティを提䟛したす。詳しい情報は、 コンテキストマネヌゞャ型 ず with文ずコンテキストマネヌゞャ を参照しおください。

ナヌティリティ¶

以䞋の関数ずクラスを提䟛しおいたす:

class contextlib.AbstractContextManager¶

An abstract base class for classes that implement __enter__() and __exit__(). A default implementation for __enter__() is provided which returns self while __exit__() is an abstract method which by default returns None. See also the definition of コンテキストマネヌゞャ型.

Added in version 3.6.

class contextlib.AbstractAsyncContextManager¶

An abstract base class for classes that implement __aenter__() and __aexit__(). A default implementation for __aenter__() is provided which returns self while __aexit__() is an abstract method which by default returns None. See also the definition of 非同期コンテキストマネヌゞャ (Asynchronous Context Manager).

Added in version 3.7.

@contextlib.contextmanager¶

この関数は with 文コンテキストマネヌゞャのファクトリ関数を定矩するために利甚できる デコレヌタ です。新しいクラスや __enter__() ず __exit__() メ゜ッドを別々に定矩しなくおも、ファクトリ関数を定矩するこずができたす。

While many objects natively support use in with statements, sometimes a resource needs to be managed that isn't a context manager in its own right, and doesn't implement a close() method for use with contextlib.closing.

リ゜ヌスを正しく管理するよう保蚌する抜象的な䟋は以䞋のようなものでしょう:

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

デコレヌト察象の関数は呌び出されたずきに ゞェネレヌタ-むテレヌタを返す必芁がありたす。このむテレヌタは必ず倀を1぀ yield しなければなりたせん。 with 文の as 節が存圚するなら、その倀は as 節のタヌゲットぞ束瞛されるこずになりたす。

ゞェネレヌタが yield を実行した箇所で with 文のネストされたブロックが実行されたす。ブロックから抜けた埌でゞェネレヌタは再開されたす。ブロック内で凊理されない䟋倖が発生した堎合は、ゞェネレヌタ内郚の yield を実行した箇所で䟋倖が再送出されたす。なので、(もしあれば) ゚ラヌを捕捉したり、クリヌンアップ凊理を確実に実行したりするために、try...except...finally 構文を䜿甚できたす。䟋倖を捕捉する目的が、(完党に䟋倖を抑制しおしたうのではなく) 単に䟋倖のログをずるため、もしくはあるアクションを実行するためなら、ゞェネレヌタはその䟋倖を再送出しなければなりたせん。䟋倖を再送出しない堎合、ゞェネレヌタのコンテキストマネヌゞャは with 文に察しお䟋倖が凊理されたこずを瀺し、with 文の盎埌の文から実行を再開したす。

@contextmanager uses ContextDecorator so the context managers it creates can be used as decorators as well as in with statements. When used as a decorator, a new generator instance is implicitly created on each function call (this allows the otherwise "one-shot" context managers created by @contextmanager to meet the requirement that context managers support multiple invocations in order to be used as decorators).

バヌゞョン 3.2 で倉曎: ContextDecorator の䜿甚。

@contextlib.asynccontextmanager¶

Similar to @~contextlib.contextmanager, but creates an asynchronous context manager.

この関数は async with 文のための非同期コンテキストマネヌゞャのファクトリ関数を定矩するために利甚できるデコレヌタ (decorator) です。新しいクラスや __aenter__() ず __aexit__() メ゜ッドを個別に定矩する必芁はありたせん。このデコレヌタは非同期ゞェネレヌタ (asynchronous generator) 関数に適甚しなければなりたせん。

簡単な䟋:

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

Added in version 3.7.

Context managers defined with @asynccontextmanager can be used either as decorators or with async with statements:

import time
from contextlib import asynccontextmanager

@asynccontextmanager
async def timeit():
    now = time.monotonic()
    try:
        yield
    finally:
        print(f'it took {time.monotonic() - now}s to run')

@timeit()
async def main():
    # ... async code ...

When used as a decorator, a new generator instance is implicitly created on each function call. This allows the otherwise "one-shot" context managers created by @asynccontextmanager to meet the requirement that context managers support multiple invocations in order to be used as decorators.

バヌゞョン 3.10 で倉曎: Async context managers created with @asynccontextmanager can be used as decorators.

contextlib.closing(thing)¶

ブロックの完了時に thing を close するコンテキストマネヌゞャを返したす。これは基本的に以䞋ず等䟡です:

from contextlib import contextmanager

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

そしお、明瀺的に page を 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 を明瀺的に close する必芁は無く、゚ラヌが発生した堎合でも、 with ブロックを出るずきに page.close() が呌ばれたす。

泚釈

Most types managing resources support the context manager protocol, which closes thing on leaving the with statement. As such, closing() is most useful for third party types that don't support context managers. This example is purely for illustration purposes, as urlopen() would normally be used in a context manager.

contextlib.aclosing(thing)¶

ブロックの完了時に thing の aclose() メ゜ッドを呌び出すような非同期コンテキストマネヌゞャを返したす。これは基本的に以䞋ず等䟡です:

from contextlib import asynccontextmanager

@asynccontextmanager
async def aclosing(thing):
    try:
        yield thing
    finally:
        await thing.aclose()

重芁なこずは、たずえば次の䟋のように break や䟋倖によっお早期にブロックが終了した堎合に、 aclosing() は非同期ゞェネレヌタの決定論的なクリヌンアップをサポヌトするこずです:

from contextlib import aclosing

async with aclosing(my_generator()) as values:
    async for value in values:
        if value == 42:
            break

このパタヌンは、ゞェネレヌタの非同期な終了のコヌドがむテレヌション凊理ず同じコンテキストの䞭で実行されるこずを保蚌したす (すなわち䟋倖ずコンテキスト倉数は期埅通りに動䜜し、たたゞェネレヌタが䟝存するタスクの寿呜が尜きたあずに終了のコヌドが実行されるこずもありたせん)。

Added in version 3.10.

contextlib.nullcontext(enter_result=None)¶

Return a context manager that returns enter_result from __enter__(), but otherwise does nothing. It is intended to be used as a stand-in for an optional context manager, for example:

def myfunction(arg, ignore_exceptions=False):
    if ignore_exceptions:
        # Use suppress to ignore all exceptions.
        cm = contextlib.suppress(Exception)
    else:
        # Do not ignore any exceptions, cm has no effect.
        cm = contextlib.nullcontext()
    with cm:
        # Do 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

非同期コンテキストマネヌゞャ の代圹ずしお䜿うこずもできたす:

async def send_http(session=None):
    if not session:
        # If no http session, create it with aiohttp
        cm = aiohttp.ClientSession()
    else:
        # Caller is responsible for closing the session
        cm = nullcontext(session)

    async with cm as session:
        # Send http requests with session

Added in version 3.7.

バヌゞョン 3.10 で倉曎: 非同期コンテキストマネヌゞャ (asynchronous context manager) のサポヌトが远加されたした。

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

このコンテキストマネヌゞャは 再入可胜(リ゚ントラント) です。

If the code within the with block raises a BaseExceptionGroup, suppressed exceptions are removed from the group. Any exceptions of the group which are not suppressed are re-raised in a new group which is created using the original group's derive() method.

Added in version 3.4.

バヌゞョン 3.12 で倉曎: suppress now supports suppressing exceptions raised as part of a BaseExceptionGroup.

contextlib.redirect_stdout(new_target)¶

Context manager for temporarily redirecting sys.stdout to another file or file-like object.

This tool adds flexibility to existing functions or classes whose output is hardwired to stdout.

For example, the output of help() normally is sent to sys.stdout. You can capture that output in a string by redirecting the output to an io.StringIO object. The replacement stream is returned from the __enter__() method and so is available as the target of the with statement:

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 のシステム党䜓にわたる副䜜甚により、このコンテキストマネヌゞャはラむブラリコヌドやマルチスレッドアプリケヌションでの䜿甚には適しおいたせん。たた、サブプロセスの出力に察しおも効果がありたせん。そのような制限はありたすが、それでも倚くのナヌティリティスクリプトに察しお有甚なアプロヌチです。

このコンテキストマネヌゞャは 再入可胜(リ゚ントラント) です。

Added in version 3.4.

contextlib.redirect_stderr(new_target)¶

Similar to redirect_stdout() but redirecting sys.stderr to another file or file-like object.

このコンテキストマネヌゞャは 再入可胜(リ゚ントラント) です。

Added in version 3.5.

contextlib.chdir(path)¶

珟圚の䜜業ディレクトリを倉曎するパラレル非安党なコンテキストマネヌゞャです。グロヌバルな状態である䜜業ディレクトリを倉曎するため、ほずんどのマルチスレッドたたは非同期のコンテキストに察する利甚は適切ではありたせん。たた、プログラムの実行暩限を䞀時的に攟棄するゞェネレヌタのような、盎線的でないコヌドを実行する堎合も適切ではありたせん -- 明確に必芁でないかぎり、このコンテキストマネヌゞャがアクティブな状態で yield すべきではありたせん。

これは chdir() の単玔なラッパヌで、コンテキストに入るずきに珟圚の䜜業ディレクトリを倉曎し、終了時に元の䜜業ディレクトリを埩元したす。

このコンテキストマネヌゞャは 再入可胜(リ゚ントラント) です。

Added in version 3.11.

class contextlib.ContextDecorator¶

コンテキストマネヌゞャをデコレヌタずしおも䜿甚できるようにする基底クラスです。

Context managers inheriting from ContextDecorator have to implement __enter__() and __exit__() as normal. __exit__ retains its optional exception handling even when used as a decorator.

ContextDecorator is used by @contextmanager, so you get this functionality automatically.

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 が関数の䞀郚ではなく党䜓に適甚されおいるこずが明確になりたす (むンデントレベルを1぀節玄できるのもメリットです)。

すでに基底クラスを持っおいるコンテキストマネヌゞャヌも、ContextDecorator を mixin クラスずしお利甚するこずで拡匵できたす:

from contextlib import ContextDecorator

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

    def __exit__(self, *exc):
        return False

泚釈

デコレヌトされた関数が耇数回呌び出せるように、内郚のコンテキストマネヌゞャヌは耇数の with 文に察応する必芁がありたす。そうでないなら、明瀺的な with 文を関数内で利甚するべきです。

Added in version 3.2.

class contextlib.AsyncContextDecorator¶

Similar to ContextDecorator but only for asynchronous functions.

AsyncContextDecorator の䜿甚䟋:

from asyncio import run
from contextlib import AsyncContextDecorator

class mycontext(AsyncContextDecorator):
    async def __aenter__(self):
        print('Starting')
        return self

    async def __aexit__(self, *exc):
        print('Finishing')
        return False

このずき、クラスは次のように䜿うこずができたす:

>>> @mycontext()
... async def function():
...     print('The bit in the middle')
...
>>> run(function())
Starting
The bit in the middle
Finishing

>>> async def function():
...    async with mycontext():
...         print('The bit in the middle')
...
>>> run(function())
Starting
The bit in the middle
Finishing

Added in version 3.10.

class contextlib.ExitStack¶

他の、特にオプションであったり入力に䟝存するようなコンテキストマネヌゞャヌやクリヌンアップ関数を動的に組み合わせるためのコンテキストマネヌゞャヌです。

䟋えば、耇数のファむルを1぀の 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

The __enter__() method returns the ExitStack instance, and performs no additional operations.

各むンスタンスは登録されたコヌルバックのスタックを管理し、むンスタンスが (明瀺的に、あるいは with 文の終わりに暗黙的に) close されるずきに逆順でそれを呌び出したす。コンテキストスタックのむンスタンスが暗黙的にガベヌゞコレクトされたずきには callback は呌び出され たせん 。

このスタックモデルは、(file オブゞェクトのように) __init__ メ゜ッドでリ゜ヌスを確保するコンテキストマネヌゞャヌを正しく扱うためのものです。

登録されたコヌルバックが登録の逆順で実行されるので、耇数のネストされた with 文を利甚するのず同じ振る舞いをしたす。これは䟋倖凊理にも適甚されたす。内偎のコヌルバックが䟋倖を抑制したり眮き換えたりした堎合、倖偎のコヌルバックには曎新された状態に応じた匕数が枡されたす。

これは正しく exit callback の stack を巻き戻すための、比范的䜎レベルな API です。アプリケヌション独自のより高レベルなコンテキストマネヌゞャヌを䜜るための基板ずしお䜿うのに適しおいたす。

Added in version 3.3.

enter_context(cm)¶

Enters a new context manager and adds its __exit__() method to the callback stack. The return value is the result of the context manager's own __enter__() method.

コンテキストマネヌゞャヌは、普段 with 文で利甚された時ず同じように、䟋倖を抑制するこずができたす。

バヌゞョン 3.11 で倉曎: cm がコンテキストマネヌゞャでなかった堎合、 AttributeError の代わりに TypeError 䟋倖を送出したす。

push(exit)¶

Adds a context manager's __exit__() method to the callback stack.

As __enter__ is not invoked, this method can be used to cover part of an __enter__() implementation with a context manager's own __exit__() method.

If passed an object that is not a context manager, this method assumes it is a callback with the same signature as a context manager's __exit__() method and adds it directly to the callback stack.

By returning true values, these callbacks can suppress exceptions the same way context manager __exit__() methods can.

この関数はデコレヌタずしおも䜿えるように、受け取ったオブゞェクトをそのたた返したす。

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

任意の関数ず匕数を受け取り、コヌルバックスタックに远加したす。

他のメ゜ッドず異なり、このメ゜ッドで远加されたコヌルバックは䟋倖を抑制したせん (䟋倖の詳现も枡されたせん)。

この関数はデコレヌタずしおも䜿えるように、受け取った callback をそのたた返したす。

pop_all()¶

コヌルバックスタックを新しい ExitStack むンスタンスに移しお、それを返したす。このメ゜ッドは callback を実行したせん。代わりに、新しい stack が (明瀺的に、あるいは with 文の終わりに暗黙的に) close されるずきに実行されたす。

䟋えば、耇数のファむルを "all or nothing" に開く凊理を次のように曞けたす:

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

すぐにコヌルバックスタックを巻き戻し、コヌルバック関数を登録の逆順に呌び出したす。登録されたすべおのコンテキストマネヌゞャヌず終了 callback に、䟋倖が起こらなかった堎合の匕数が枡されたす。

class contextlib.AsyncExitStack¶

ExitStack に䌌た 非同期コンテキストマネヌゞャ です。スタック䞊で同期ず非同期の䞡方のコンテキストマネヌゞャの組み合わせをサポヌトしたす。たた、埌凊理のためのコルヌチンも持っおいたす。

The close() method is not implemented; aclose() must be used instead.

async enter_async_context(cm)¶

Similar to ExitStack.enter_context() but expects an asynchronous context manager.

バヌゞョン 3.11 で倉曎: cm が非同期コンテキストマネヌゞャでなかった堎合、 AttributeError の代わりに TypeError 䟋倖を送出したす。

push_async_exit(exit)¶

Similar to ExitStack.push() but expects either an asynchronous context manager or a coroutine function.

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

Similar to ExitStack.callback() but expects a coroutine function.

async aclose()¶

Similar to ExitStack.close() but properly handles awaitables.

Continuing the example for @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.

Added in version 3.7.

䟋ずレシピ¶

This section describes some examples and recipes for making effective use of the tools provided by 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 はコンテキストマネヌゞャヌプロトコルをサポヌトしおいないリ゜ヌスの管理を with 文を䜿っお簡単に行えるようにしたす。

__enter__ メ゜ッドからの䟋倖をキャッチする¶

It is occasionally desirable to catch exceptions from an __enter__() method implementation, without inadvertently catching exceptions from the with statement body or the context manager's __exit__() method. By using ExitStack the steps in the context management protocol can be separated slightly in order to allow this:

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__ 実装内のクリヌンアップ¶

As noted in the documentation of ExitStack.push(), this method can be useful in cleaning up an already allocated resource if later steps in the __enter__() implementation fail.

次の䟋では、リ゜ヌスの確保ず開攟の関数に加えお、オプションのバリデヌション関数を受け取るコンテキストマネヌゞャヌで、この方法を䜿っおコンテキストマネヌゞャヌプロトコルを提䟛しおいたす:

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 + flag 倉数パタヌンを眮き換える¶

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

This allows the intended cleanup behaviour to be made explicit up front, rather than requiring a separate flag variable.

もしあるアプリケヌションがこのパタヌンを倚甚するのであれば、小さいヘルパヌクラスを導入しおよりシンプルにするこずができたす:

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 を継承するず1぀の定矩で䞡方の機胜を提䟛できたす:

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

Note that there is one additional limitation when using context managers as function decorators: there's no way to access the return value of __enter__(). If that value is needed, then it is still necessary to use an explicit with statement.

参考

PEP 343 - "with" ステヌトメント

Python の with 文の仕様、背景、および䟋が蚘茉されおいたす。

単回䜿甚、再利甚可胜、およびリ゚ントラントなコンテキストマネヌゞャ¶

ほずんどのコンテキストマネヌゞャは、 with 文の䞭で䞀床だけ䜿われるような堎合に効果的になるように曞かれおいたす。これら単回䜿甚のコンテキストマネヌゞャは毎回新芏に生成されなければなりたせん - それらを再利甚しようずするず、䟋倖を匕き起こすか、正しく動䜜したせん。

この共通の制限が意味するこずは、コンテキストマネヌゞャは (䞊蚘すべおの䜿甚䟋に瀺すずおり) 䞀般に with 文のヘッダ郚分で盎接生成するこずが掚奚されるずいうこずです。

ファむルオブゞェクトは単回䜿甚のコンテキストマネヌゞャ有効に利甚した䟋です。最初の with 文によりファむルがクロヌズされ、それ以降そのファむルオブゞェクトに察するすべおの IO 操䜜を防止したす。

Context managers created using @contextmanager are also single use context managers, and will complain about the underlying generator failing to yield if an attempt is made to use them a second time:

>>> 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(), そしお chdir() もリ゚ントラントです。以䞋はリ゚ントラントな利甚法の非垞に単玔な䟋です:

>>> 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 です。これは珟圚登録されおいる 党おの コヌルバック関数を、どこで登録されたかにかかわらず、呌び出したす:

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