Имеет ли Python эквивалент функций Haskell "mask" или "bracket"?
В Haskell у нас есть асинхронные исключения; мы можем использовать throwTo
, чтобы поднять любое исключение в другом потоке:
throwTo :: Exception e => ThreadId -> e -> IO ()
throwTo
вызывает произвольное исключение в целевом потоке (только GHC).
Чтобы иметь возможность писать код с такими гарантиями, как "всегда будет выходить из замка после его приобретения", у нас mask
для запуска код, в котором асинхронные исключения могут приниматься только при блокировке вычислений:
mask :: ((forall a. IO a -> IO a) -> IO b) -> IO b
Выполняет вычисление ввода-вывода с асинхронными исключениями в масках. То есть любой поток, который пытается поднять исключение в текущем потоке с помощью throwTo
, будет заблокирован до тех пор, пока асинхронные исключения не будут снова разобраны.
и более сильный uninterruptibleMask
, в котором исключения async вообще не будут возникать при маскированном вычислении:
uninterruptibleMask :: ((forall a. IO a -> IO a) -> IO b) -> IO b
Подобно mask
, но маскированное вычисление не прерывается
Маскировка используется для реализации абстракций более высокого уровня, таких как bracket
:
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
Когда вы хотите приобрести ресурс, выполните некоторую работу с ним, а затем отпустите ресурс, рекомендуется использовать bracket
, потому что bracket
установит необходимый обработчик исключений, чтобы освободить ресурс в что исключение возникает при вычислении. Если возникает исключение, то bracket
будет повторно поднимать исключение (после выполнения выпуска).
Если я правильно понимаю, Python имеет (менее общую) форму асинхронных исключений, причем наиболее заметным проявлением является KeyboardInterrupt
:
Поднимается, когда пользователь нажимает клавишу прерывания (обычно Control - C или Delete). Во время выполнения проверка прерываний выполняется регулярно.
Документация неточна, когда может произойти "проверка прерываний", но, похоже, подразумевается, что KeyboardInterrupt
может быть поднят в любой момент выполнения программы. Таким образом, кажется, что исключения на асинхронном языке Python приходят со всеми теми же трудностями, что и при сохранении правильности.
Например, рассмотрим такой шаблон:
x = None
try:
x = acquire()
do_something(x) # (1)
finally:
if x is not None: # (2)
release(x)
Если какое-либо исключение возникает во время (1)
, то мы уверены, что будет выполняться содержимое блока finally
. Но что произойдет, если a KeyboardInterrupt
находится во время (2)
?
Кажется принципиально невозможным гарантировать очистку ресурсов при наличии исключений asyc без способа их маскировки. Есть ли какое-то средство для этого, или мы полагаемся на страусный алгоритм?
Ответы
Ответ 1
Это контекстные менеджеры.
with acquire() as x:
do_something(x)
acquire
возвращает объект, тип которого определяет метод __enter__
, который возвращает значение, привязанное к x
, и метод __exit__
, который выполняется в конце инструкции with
независимо от того, как утверждение завершено.