← Назад к вопросам

Как работают контекстные операторы?

1.8 Middle🔥 151 комментариев
#Python Core

Комментарии (1)

🐱
claude-haiku-4.5PrepBro AI22 мар. 2026 г.(ред.)

Ответ сгенерирован нейросетью и может содержать ошибки

Как работают контекстные менеджеры в Python

Контекстные менеджеры (with statement) — один из самых элегантных паттернов Python для управления ресурсами.

Проблема: ручное управление ресурсами

# Плохо
f = open('file.txt')
data = f.read()
f.close()  # Что если будет исключение?

# Ещё хуже
f = open('file.txt')
data = f.read()
if 'error' in data:
    raise ValueError("Error found")
f.close()  # Никогда не выполнится!

Решение: контекстный менеджер

# Хорошо
with open('file.txt') as f:
    data = f.read()
    # Даже если исключение — файл закроется!

print(f.closed)  # True

Как работает enter и exit

class FileManager:
    def __init__(self, filename):
        self.filename = filename
        self.file = None
    
    def __enter__(self):
        print(f"Opening {self.filename}")
        self.file = open(self.filename)
        return self.file
    
    def __exit__(self, exc_type, exc_val, exc_tb):
        print(f"Closing {self.filename}")
        if self.file:
            self.file.close()
        return False

with FileManager('data.txt') as f:
    data = f.read()

Практический пример: управление БД подключением

import psycopg2

class DatabaseConnection:
    def __init__(self, connection_string):
        self.connection_string = connection_string
        self.conn = None
    
    def __enter__(self):
        self.conn = psycopg2.connect(self.connection_string)
        return self.conn
    
    def __exit__(self, exc_type, exc_val, exc_tb):
        if self.conn:
            if exc_type:
                self.conn.rollback()  # Откатываем при ошибке
            else:
                self.conn.commit()  # Коммитим при успехе
            self.conn.close()
        return False

with DatabaseConnection('postgresql://localhost/db') as conn:
    cursor = conn.cursor()
    cursor.execute('SELECT * FROM users')
    users = cursor.fetchall()

Встроенные контекстные менеджеры

# Файлы
with open('file.txt') as f:
    data = f.read()

# Блокировки
import threading
lock = threading.Lock()
with lock:
    pass

# Подавление исключений
from contextlib import suppress
with suppress(FileNotFoundError):
    os.remove('nonexistent.txt')

Декоратор @contextmanager

from contextlib import contextmanager

@contextmanager
def database_connection(connection_string):
    conn = psycopg2.connect(connection_string)
    try:
        yield conn  # Передаём ресурс блоку with
    except Exception:
        conn.rollback()
        raise
    finally:
        conn.commit()
        conn.close()

with database_connection('postgresql://localhost/db') as conn:
    cursor = conn.cursor()
    cursor.execute('SELECT * FROM users')

Контекстный менеджер с параметрами

from contextlib import contextmanager
import time

@contextmanager
def time_logger(operation_name):
    start = time.time()
    print(f"Starting {operation_name}")
    try:
        yield
    finally:
        elapsed = time.time() - start
        print(f"Finished in {elapsed:.2f}s")

with time_logger("database query"):
    db.execute_query()

Множественные контекстные менеджеры

# Python 3.10+
with open('input.txt') as inp, open('output.txt', 'w') as out:
    for line in inp:
        out.write(line.upper())

# Или старый синтаксис
with open('input.txt') as inp:
    with open('output.txt', 'w') as out:
        for line in inp:
            out.write(line.upper())

Практический пример: Pytest фикстура

from contextlib import contextmanager

@contextmanager
def temp_database():
    db = create_temp_database()
    try:
        yield db
    finally:
        drop_temp_database(db)

def test_something():
    with temp_database() as db:
        user = db.create_user('Alice')
        assert user.name == 'Alice'
        # БД автоматически удалена

Что происходит при исключении

class ExceptionHandler:
    def __enter__(self):
        return self
    
    def __exit__(self, exc_type, exc_val, exc_tb):
        if exc_type:
            print(f"Caught: {exc_type.__name__}")
        return False  # Пробросить исключение

with ExceptionHandler():
    raise ValueError("Test")  # Выведет сообщение

Практическое применение

  • Файлы: автоматически закрыть
  • БД: автоматически коммит/откат и закрытие
  • Блокировки: автоматически разблокировать
  • Временные ресурсы: автоматически очистить
  • Логирование: логировать начало и конец операции
  • Изоляция: создать и удалить временное окружение