32

別のコンテキストマネージャー内で作成されたコンテキストマネージャーは、Python でどのように処理する必要がありますか?

例:コンテキスト マネージャーとして機能するクラスと、コンテキスト マネージャーとしても機能するAクラスBがあるとします。ただし、クラスBインスタンスはインスタンス化して、クラスのインスタンスを使用する必要がありますA。私は PEP 343 を経験しましたが、これが私が考えた解決策です:

class A(object):
    def __enter__(self):
        # Acquire some resources here
        return self

    def __exit__(seplf, exception_type, exception, traceback):
        # Release the resources and clean up
        pass


class B(object):
    def __init__(self):
        self.a = A()

    def __enter__(self):
        # Acquire some resources, but also need to "start" our instance of A
        self.a.__enter__()
        return self

    def __exit__(self, exception_type, exception, traceback):
        # Release the resources, and make our instance of A clean up as well
        self.a.__exit__(exception_type, exception, traceback)

これは正しいアプローチですか?それとも、いくつかの落とし穴がありませんか?

4

3 に答える 3

12

デコレータを使用できれば、@contextlib.contextmanagerあなたの生活はずっと楽になります:

import contextlib

@contextlib.contextmanager
def internal_cm():
    try:
        print "Entering internal_cm"
        yield None
        print "Exiting cleanly from internal_cm"
    finally:
        print "Finally internal_cm"


@contextlib.contextmanager
def external_cm():
    with internal_cm() as c:
        try:
            print "In external_cm_f", c
            yield [c]
            print "Exiting cleanly from external_cm_f", c
        finally:
            print "Finally external_cm_f", c


if "__main__" == __name__:
    with external_cm():
        print "Location A"
    print
    with external_cm():
        print "Location B"
        raise Exception("Some exception occurs!!")
于 2015-02-09T00:57:11.103 に答える
9

または、次のようにコードを書くこともできます。

with A() as a:
    with B(a) as b:
        # your code here

試してみたい別の解決策は次のとおりです。

class A:

    def __init__(self):
        pass

    def __enter__(self):
        return self

    def __exit__(self, exc_type, exc_val, exc_tb):
        pass

class B(A):

    def __init__(self):
        super().__init__()

    def __enter__(self):
        super().__enter__()
        return self

    def __exit__(self, exc_type, exc_val, exc_tb):
        super().__exit__(exc_type, exc_val, exc_tb)

あなたの状況の説明を考慮した後、これはより良い解決策かもしれません:

class Resource:

    def __init__(self, dependency=None):
        self.dependency = dependency
        # your code here

    def __enter__(self):
        if self.dependency:
            self.dependency.__enter__()
        # your code here
        return self

    def __exit__(self, exc_type, exc_val, exc_tb):
        # your code here
        if self.dependency:
            self.dependency.__exit__(exc_type, exc_val, exc_tb)

次の実装が正しいかどうかはわかりませんが__exit__、例外を適切に処理する必要があります。例外を適切に処理しながら、呼び出しを再帰的に連鎖させる方法を想像するのは少し難しいです。

class Resource:

    def __init__(self, dependency=None):
        self.dependency = dependency
        self.my_init()

    def __enter__(self):
        if self.dependency:
            self.dependency.__enter__()
        return self.my_enter()

    def __exit__(self, exc_type, exc_val, exc_tb):
        suppress = False
        try:
            suppress = self.my_exit(exc_type, exc_val, exc_tb)
        except:
            exc_type, exc_val, exc_tb = sys.exc_info()
        if suppress:
            exc_type = exc_val = exc_tb = None
        if self.dependency:
            suppress = self.dependeny.__exit__(exc_type, exc_val, exc_tb)
            if not supress:
                raise exc_val.with_traceback(exc_tb) from None
        return suppress

    def my_init(self):
        pass

    def my_enter(self):
        pass

    def my_exit(self, exc_type, exc_val, exc_tb):
        pass
于 2014-07-08T16:43:04.620 に答える
1

以下は、contextmanager での手動リソース管理の例です。 外部の contextmanager が内部を管理します。

class Inner:

    def __enter__(self):
        print("<inner>")
        return self

    def __exit__(self, exc_type, exc_value, traceback):
        print("</inner>")


class Outer:

    def __init__(self):
        self.inner = Inner()

    def __enter__(self):
        self.inner.__enter__()
        try:
            #raise RuntimeError("Suppose we fail here")
            print("<outer>")
            return self
        except Exception as e:
            self.inner.__exit__(None, None, None)
            raise e

    def __exit__(self, exc_type, exc_value, traceback):
        print("</outer>")
        self.inner.__exit__(exc_type, exc_value, traceback)

使用法は通常どおりです。

with Outer() as scope:
    #raise RuntimeError("Suppose we fail here")
    pass

注意深い読者は、内部の contextmanager が無意味な操り人形になっていることに気付くでしょう (手動でスレッドを引っ張っているため)。それでいい。

于 2018-06-27T08:38:28.233 に答える