Розбираємося з оператором `with`

2 хв. читання

У цій частині циклу статей про синтаксичний цукор Python ми детально розглянемо роботу оператора with.

Переглядаючи байт-код для простого оператора with, ви помітите, що застосовується багато інструкцій opcode. Більшість із них — це керування стеком виконання для інтерпретатора та обробка можливих винятків. Тому ми не зачіпатимемо байт-код як такий, а натомість розглянемо пояснення синтаксису, як це показано у посиланні:

# with a as b:
#     c

_enter = type(a).__enter__
_exit = type(a).__exit__
b = _enter(a)

try:
    c
except:
    if not _exit(a, *sys.exc_info()):
        raise
else:
    _exit(a, None, None, None)

Розв'язання з оператором with

Щоб проілюструвати, як все це працює, ми скористаємося класичним прикладом замикання (lock) RAII у цьому дописі. RAII означає «Отримання ресурсу є ініціалізація». Тобто під час створення об'єкта щось відбувається, а під час вивільнення/видалення об'єкта щось скасовується.

У замиканнях це означає отримання замикання (алокацію) під час створення, а потім його звільнення (деалокацію) під час виділення. Python як мова не має гарантованої семантики очищення з погляду часу та порядку. Тому були введені контекстні менеджери, щоб явно додати до Python мовну конструкцію, яка дає нам переваги концепції RAII.

import threading

lock = threading.Lock()

with lock:  # Замикання утримується.
    pass  # Щось, що потребує замикання.
# Замикання починає діяти.

Існує дві частини для контекстного менеджера. Одна з них викликає його спеціальним методом __enter__ під час введення блоку with; у нашому прикладі замикання це викликає threading.Lock.acquire(). Якщо ви застосовуєте умову as контекстного менеджера, об'єкт, який повертає метод __enter__ прив'язується до вказаної нами змінної.

Друга частина контекстного менеджера — це спеціальний метод __exit__, він застосовується під час виклику блоку with. У нашому прикладі замикання це викликає threading.Lock.release().

Головна особливість __exit__ — це визначення, чи створено в тілі блоку with виняток. Якщо виняток є, він передається складовими частинами, як повертається *sys.exc_info() до __exit__; якщо виняток не створюється, тоді до цих трьох частин передається None. Якщо виняток виникає у тілі менеджера контексту, метод __exit__ може стримати чи дозволити поширення винятку.

Яку саме дію застосувати, визначається тим, чи повертає метод значення true чи false відповідно. В цьому полягає завдання поверненого значення, тож винятки будуть відновлені завдяки тому, що методи типово повертають None.

Якщо ви хотіли б дізнатися більше історичних відомостей, PEP 343 — це те, що додали контекстні менеджери до Python.

Помітили помилку? Повідомте автору, для цього достатньо виділити текст з помилкою та натиснути Ctrl+Enter
Codeguida 5.8K
Приєднався: 8 місяців тому
Коментарі (0)

    Ще немає коментарів

Щоб залишити коментар необхідно авторизуватися.

Вхід / Реєстрація