【问题标题】:Python accessing parent class's "with" statement in an overridden methodPython 在重写方法中访问父类的“with”语句
【发布时间】:2020-12-21 20:21:58
【问题描述】:

我有一个基类,其方法使用with 语句。在子类中,我重写了相同的方法,然后想访问相同的 with 语句(而不是两个 with 语句)。

解决这个问题的标准方法是什么?

有关示例和可能的解决方案,请参见下文。


示例使用threading.Lock

from threading import Lock


class BaseClass:
    def __init__(self):
        self.lock = Lock()
        self._data = 0

    def do_something_locked(self) -> None:
        with self.lock:
            self._data += 5


class ChildClass(BaseClass):
    def do_something_locked(self) -> None:
        super().do_something_locked()
        # Obviously the parent class's self.lock's __exit__ method has 
        # already been called.  What are accepted methods to add more 
        # functionality inside parent class's "with" statement?
        with self.lock:
            self._data += 1

可能的解决方案

我的第一个倾向是在BaseClass 中定义一个私有方法,如下所示:

    def do_something_locked(self) -> None:
        with self.lock:
            self._do_something()

    def _do_something(self) -> None:
        self._data += 5

然后ChildClass 可以覆盖_do_something。这样可以正常工作。

我想知道,还有其他解决这个问题的常见模式吗?

【问题讨论】:

    标签: python oop with-statement contextmanager


    【解决方案1】:

    我的第一个倾向是像这样在BaseClass 中定义一个私有方法......然后ChildClass 可以覆盖_do_something。这样可以正常工作。

    这是解决问题的好方法,even when you don't have a special requirement(就像需要保持在 with 块上下文中)。我不会在“钩子”方法名称中使用前导下划线,因为您期望在派生类中被覆盖的任何东西在逻辑上都是类接口的一部分。另外,如果self._data += 5 部分总是需要发生,则将其保留在do_something_locked 中。

    还有其他解决这个问题的常见模式吗?

    针对问题,您可以使用重入锁,如其他答案所示。您也可以忽略类相关的事实,并使用依赖注入 - 在基类中创建一个通用方法,该方法接受可调用并使用锁执行它:

    # in base class
    def do_locked(self, what, *args, **kwargs):
        with self.lock:
            what(*args, **kwargs)
    
    # in derived class
    def _implementation(self):
        pass
    def do_interesting_thing(self):
        # pass in our own bound method, which takes no arguments
        self._do_locked(self._implementation)
    

    这种方式允许客户端代码以自定义方式使用锁。如果您不需要或不想要该功能,这可能不是一个好主意。

    【讨论】:

      【解决方案2】:

      使用可重入锁。这将自动“连接”嵌套的with 语句,仅在最外层的with 之后释放锁。

      from threading import RLock
      
      
      class BaseClass:
          def __init__(self):
              self.lock = RLock()
              self._data = 0
      
          def do_something_locked(self) -> None:
              with self.lock:
                  self._data += 5
      
      
      class ChildClass(BaseClass):
          def do_something_locked(self) -> None:
              with self.lock:
                  super().do_something_locked()
                  self._data += 1
      

      一般来说,reentrant context managers 的模式明确存在以允许可能嵌套的上下文。

      这些上下文管理器不仅可以在多个 with 语句中使用,还可以在已经使用同一个上下文管理器的 with 语句中使用。

      【讨论】:

        猜你喜欢
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        • 2019-05-18
        • 2021-11-16
        • 2020-03-15
        • 2015-06-29
        • 2016-07-22
        相关资源
        最近更新 更多