3

Python コンテキスト マネージャーが実行できるすべてのことを適切な場所に配置する方法に困惑しています。

私が理解しているように、コンテキストマネージャーを構築する可能性のある要素には次のものがあります。

  • A:いつも起こること
  • B: C に必要な準備
  • C: コンテキストで使用されるオブジェクト X を作成して確立する
  • D: コンテキストの開始前に X の確立に成功したことを使用して、いくつかのことを行います
  • E: X をコンテキストに戻します ( で使用するためas)
  • F: コンテキストの最後ですべて問題がなければ、X で終了します
  • G: コンテキストに入る前に、C と B での失敗の結果に対処する
  • H: 状況に応じて失敗の結果に対処する

これらの各要素がコンテキストマネージャー関数のどこにあるのかはおおまかにわかると思いますが、それらをクラスに配置する方法については完全に途方に暮れています。

これらの要素のそれぞれが関数と(特に)クラスの両方に含まれていることを示すコンテキストマネージャー関数とクラスのテンプレートはありますか? ここや他の場所で多くの例を調べましたが、包括的なものはなく、実際のコードを使用しているものも多く、上記の各構成要素に常にマップできるとは限りません。


関数を介して実装された場合のコンテキストマネージャーの動作を基本的に理解していると思います。

from contextlib import contextmanager     
@contextmanager
def log_file_open(oec_data, build_description, log_dir):
    # A: Something that always happens
    try:
        # B: Some stuff needed to make a_thing
        a_thing = establish_thing_in_a_way_that_might_fail() # C
        # D: Some things that happen using a_thing at context start
        yield a_thing # E
        # F: Wrap up with a_thing when all is well
    except:
        # G: Deal the consequences of failure in try or...
        # H: Deal the consequences of failure in context
    finally:
        # Could F go here instead?

たとえば、正常に開いて閉じたときに何かを書き込む必要があるが、問題が発生した場合はクリーンアップする必要があるファイルを開くには、次のように記述できます。

from contextlib import contextmanager     
@contextmanager
def log_file_open(oec_data, build_description, log_dir):
    print('Entering context...')
    try:
        usable_file_name = get_some_name()
        a_thing =  open(usable_file_name, mode='w')
        a_thing.write('Logging context started.')
        yield a_thing
        a_thing.write('Logging context ended.')
    except:
        a_thing.close()
        os.remove(a_thing.name)
        raise

しかし、これが正しいかどうかはわかりません。また、クラスの使用__enter()____exit()__クラス内での使用にどのように対応するのか混乱しています。それは(概略的に)ですか:

def __init__(self):
    # A: Something that always happens

def __enter__(self):
    try:
        # B: Some stuff needed to make a_thing
        a_thing = establish_thing_in_a_way_that_might_fail() # C
        # D: Some things that happen using a_thing at context start
     except:
        # G: Deal the consequences of failure in try
        a_thing = some_appropriate_blank_value
     finally:
        return a_thing # E

 def __exit__(self, type, value, traceback):
        if type is None:
            # F: Wrap up with a_thing when all is well
            return True
        else:
            # H: Deal the consequences of failure in context
            return False
4

2 に答える 2

2

あなたの理解はおおむね正しいと思います。コンテキスト マネージャはオブジェクトであり、その__enter__および__exit__メソッドを通じてコン​​テキストを管理します。そのため、オブジェクトの存続期間中、何が起こるか__init__はそのままです。具体例を見てみましょう。

class CMan(object):
    def __init__(self, *parameters):
        "Creates a new context manager"
        print "Creating object..."

    def __enter__(self):
        "Enters the manager (opening the file)"
        print "Entering context..."
        a_thing = self # Or any other relevant value to be used in this context
        print "Returning %s" % a_thing
        return a_thing

    def __exit__(self, type, value, traceback):
        "Exits the context"
        if type is None:
            print "Exiting with no exception -> Wrapping up"
            return
        print "Exiting with exception %s" % type

これは次のように使用されます:

>>> with CMan(1,2,3) as x:
...     print 1 + 1
Creating object...
Entering context...
Returning <__main__.CMan object at 0x02514F70>
2
Exiting with no exception -> Wrapping up

その場でオブジェクトを作成することは必須ではないことに注意してください。

>>> mgr = CMan(1,2,3)
Creating object...
>>> with mgr as x:
...     print 1 + 1
Entering context...
Returning <__main__.CMan object at 0x02514F70>
2
Exiting with no exception -> Wrapping up

最後に、 の戻り値によって、例外を発生させる必要があるかどうか__exit__が決まります。値が(たとえば、、... )と評価された場合、例外発生します。それ以外の場合、これはコンテキスト マネージャーが例外を処理したことを意味し、例外を発生させる必要はありません。例えば:FalseFalse0None

>>> class Arithmetic(object):
...     def __enter__(self):
...         return self
...     def __exit__(self, type, value, traceback):
...         if type == ZeroDivisionError:
...             print "I dont care -> Ignoring"
...             return True
...         else:
...             print "Unknown error: Panicking !"
...             return False

>>> with Arithmetic() as a:
...     print 1 / 0 # Divide by 0
I dont care -> Ignoring

>>> with Arithmetic() as a:
...     print 1 + "2" # Type error
Unknown error: Panicking !
Traceback (most recent call last):
  File "<stdin>", line 2, in <module>
TypeError: unsupported operand type(s) for +: 'int' and 'str'

0 による除算エラーの場合、__exit__返されるようにTrue、エラーは伝播されないことに注意してください。それ以外の場合、コンテキスト マネージャーを終了した後に発生します。コンテキスト マネージャーへの呼び出しを考えることができます。

>>> with X as x:
...     f(x)

以下と同等です。

>>> x = X.__enter__()
>>> try:
...     exc = None
...     f(x)     
... except Exception as e:
...     exc = e
... finally:
...     handled = X.__exit__(exc)
...     if exc and not handled:
...         raise exc

もちろん、メソッド内またはで例外が発生した場合は、適切に処理する必要があります。たとえば、生成が失敗する可能性がある場合などです。'Python with statement' を探すと、ウェブ上で多くのリソースを見つけることができます。これは、通常、このパターンを参照する方法です (ただし、コンテキスト マネージャーの方が正確です)。__enter____exit__a_thing

于 2014-01-06T16:03:11.140 に答える