Pythons' with' -statement:使用__enter __ / __ exit__正确地嵌套/派生类

时间:2015-12-03 15:20:28

标签: python derived-class with-statement

我应该如何正确地嵌套类的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__()行为有什么不同吗?

1 个答案:

答案 0 :(得分:3)

理想情况下,请使用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')

在Python 2中,super()必须为super(class_b, self)

与您的代码相比,行为发生了变化:此代码在退出b之前退出a,这意味着范围嵌套。你已经编写了代码来按照其他顺序执行它们,尽管这很容易改变。通常它没有任何区别,但是当它确实很重要时,你通常会想要嵌套。因此,对于一个(公认的设计)示例,如果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')