Python是否有类似于Haskell的'mask'或'bracket'函数的等效功能?

14
在Haskell中,我们有异步异常;我们可以使用throwTo在另一个线程中引发任何异常:
throwTo :: Exception e => ThreadId -> e -> IO ()

throwTo raises an arbitrary exception in the target thread (GHC only).

为了能够编写具有保证的代码,例如“在获取锁之后始终会释放锁”,我们使用 mask 来运行计算过程中仅在阻塞时才能接收到异步异常的代码:
mask :: ((forall a. IO a -> IO a) -> IO b) -> IO b

Executes an IO computation with asynchronous exceptions masked. That is, any thread which attempts to raise an exception in the current thread with throwTo will be blocked until asynchronous exceptions are unmasked again.

同时还需要一个更强的uninterruptibleMask,在该掩码计算期间将完全不会触发异步异常:

uninterruptibleMask :: ((forall a. IO a -> IO a) -> IO b) -> IO b

Like mask, but the masked computation is not interruptible

遮罩是用于实现更高级的抽象,如 括号

bracket
    :: IO a         -- computation to run first ("acquire resource")
    -> (a -> IO b)  -- computation to run last ("release resource")
    -> (a -> IO c)  -- computation to run in-between
    -> IO c         -- returns the value from the in-between computation

When you want to acquire a resource, do some work with it, and then release the resource, it is a good idea to use bracket, because bracket will install the necessary exception handler to release the resource in the event that an exception is raised during the computation. If an exception is raised, then bracket will re-raise the exception (after performing the release).

如果我理解正确,Python 有一种(不太通用的)异步异常形式,最显著的表现是 KeyboardInterrupt
引发原因是用户按下中断键(通常是 Control-CDelete)。在执行过程中,会定期检查是否有中断。
文档对“检查中断”的时间并没有明确说明,但它似乎意味着 KeyboardInterrupt 可能在程序执行的 任何 点上被触发。因此,Python 的异步异常具有保持正确性的所有相同微妙困难。
例如,考虑以下模式:
x = None
try:
    x = acquire()
    do_something(x)    # (1)
finally:
    if x is not None:  # (2)
        release(x)

如果在 (1) 过程中出现任何异常,那么我们可以确保 finally 块的内容将被执行。但是如果在 (2) 过程中出现 KeyboardInterrupt 异常会发生什么呢?
在存在异步异常的情况下,似乎根本无法保证资源清理,除非有一种屏蔽它们的方法。是否有此类工具,或者我们依赖于 鸵鸟算法

1
如果你想要这样做的话,我觉得你基本上必须安装自己的Ctrl-C信号处理程序。 - user2357112
1个回答

4
这就是上下文管理器的用处。
with acquire() as x:
    do_something(x)

acquire 返回一个对象,其类型定义了一个 __enter__ 方法,该方法返回与 x 绑定的值,并且定义了一个 __exit__ 方法,在 with 语句结束时执行,无论语句如何结束。


3
如果出现其他异常,__exit__方法能够被中断吗? - Daniel Wagner
@DanielWagner 我模糊地记得我写Python时的情况:在__exit__期间发生的异常会像平常一样中断__exit__方法;引发__exit__块的异常将成为后来异常的__cause__ - Benjamin Hodgson
你确实需要自己安装信号处理程序,但是__exit__方法提供了一个方便的范围:在进入该方法时立即安装处理程序,并在退出之前恢复先前的处理程序。 - chepner
同样的问题:信号处理程序的执行是否可以被另一个信号中断? - Chris Martin

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接