【问题标题】:Pythons 'with'-statement: correctly nest/derive classes with __enter__/__exit__Python 'with'-statement:使用 __enter__/__exit__ 正确嵌套/派生类
【发布时间】:2016-03-08 06:46:22
【问题描述】:

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

【问题讨论】:

  • 我不知道有任何实用程序。你的代码有一个微妙的错误:你不能吞下class_a.__exit__ 的返回值。 (决定 __exit__ 何时返回 true/false 很重要;我无法给出更具体的建议。)
  • 在第二个版本中,您有两个独立的 class_a 实例,一个是派生的,一个是委托的。所以这不是你真正想要的。第一个例子在我看来还可以。
  • 你当然是对的,但从 class_a 派生只是一个复制/粘贴问题。
  • 这仍然没有意义,您的问题是“我应该如何正确派生类”
  • @zwol:我的直觉是class_b::__exit__ 应该在class_a::__exit__ 的情况下返回true,并且如果它出于自身原因吞下异常。但你是对的,这需要逐案考虑。

标签: python derived-class with-statement


【解决方案1】:

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

与您的代码相比,行为发生了变化:此代码在退出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')

【讨论】:

  • 你的回答是我问问题的一个例子@Stackoverflow 即使没有真正的问题:)
猜你喜欢
  • 1970-01-01
  • 2019-12-01
  • 1970-01-01
  • 2010-12-31
  • 2016-10-31
  • 2017-03-25
  • 2014-04-20
  • 1970-01-01
相关资源
最近更新 更多