Pythons 'with'-语句:正确 nest/derive 类 和 __enter__/__exit__

Pythons 'with'-statement: correctly nest/derive classes with __enter__/__exit__

我应该如何正确嵌套 类 的 with 相关行为(例如,在派生或实例化时)?

这对我有用,但我想知道是否有专门的方法来做到这一点:

class class_a:
    def __init__(self):
        print('class_a::__init__')

    def __enter__(self):
        print('class_a::__enter__')
        return self

    def __exit__(self, type, exit, tb):
        print('class_a::__exit__')


class class_b(class_a):
    def __init__(self):
        class_a.__init__(self)
        print('class_b::__init__')

    def __enter__(self):
        class_a.__enter__(self)
        print('class_b::__enter__')
        return self

    def __exit__(self, type, exit, tb):
        class_a.__exit__(self, type, exit, tb)
        print('class_b::__exit__', type, exit, tb)

with class_b():
    print('ready')
    try:
        signal.pause()
    except:
        pass

一种不同的方法是像这样实现 class_b

class class_b:
    def __init__(self):
        self._class_a_inst = class_a()
        print('class_b::__init__')

    def __enter__(self):
        self._class_a_inst.__enter__()
        print('class_b::__enter__')
        return self

    def __exit__(self, type, exit, tb):
        self._class_a_inst.__exit__(type, exit, tb)
        print('class_b::__exit__', type, exit, tb)

__enter__() / __exit__() 行为有什么区别吗?

理想情况下,使用 contextlib.contextmanager。对于求导的情况:

import contextlib

class context_mixin:
    def __enter__(self):
         self.__context = self.context()
         return self.__context.__enter__()
    def __exit__(self, *args):
         return self.__context.__exit__(*args)

class class_a(context_mixin):
    @contextlib.contextmanager
    def context(self):
         print('class_a enter')
         try:
             yield self
         finally:
             print('class_a exit')

class class_b(class_a):
    @contextlib.contextmanager
    def context(self):
        with super().context():
            print('class_b enter')
            try:
                yield self
            finally:
                print('class_b exit')

在Python2中,super()需要为super(class_b, self)

与您的代码相比,行为发生了变化:此代码在退出 a 之前退出 b,这意味着范围嵌套。您已经编写代码以按其他顺序执行它们,尽管这很容易更改。通常它没有什么区别,但当它确实重要时,你通常希望事情嵌套。因此,对于一个(公认的)示例,如果 class_a 表示一个打开的文件,而 class_b 表示某种文件格式,那么 class_a 的退出路径将关闭文件,而退出class_b 的路径将写入任何尚未提交的缓冲更改。显然 b 应该先发生!

对于持有另一个对象的情况:

class class_b(context_mixin):
    def __init__(self):
        self.a = class_a()
    @contextlib.contextmanager
    def context(self):
        with self.a:
            print('class_b enter')
            try:
                yield self
            finally:
                print('class_b exit')