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

Какие знаешь ключевые слова в контекстном менеджере?

2.0 Middle🔥 131 комментариев
#Python Core#Архитектура и паттерны

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

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

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

Ключевые слова в контекстном менеджере

Контекстный менеджер в Python — это объект, который управляет ресурсами. Основные ключевые слова: with, as, __enter__, __exit__.

Ключевое слово: with

# Открывает контекстный менеджер
with open('file.txt') as f:
    data = f.read()
# Автоматически закрывает файл

# Эквивалент (без with)
f = open('file.txt')
try:
    data = f.read()
finally:
    f.close()  # Гарантированно выполнится

Ключевое слово: as

# Привязывает результат __enter__() к переменной
with open('file.txt') as f:  # 'f' = результат __enter__()
    content = f.read()

# Можно без as
with open('file.txt'):
    pass

# Множественные менеджеры
with open('input.txt') as f_in, open('output.txt', 'w') as f_out:
    f_out.write(f_in.read())

Магические методы: __enter__ и __exit__

class MyContext:
    def __enter__(self):
        print("Entering")
        return self  # Это привязывается к переменной после 'as'
    
    def __exit__(self, exc_type, exc_val, exc_tb):
        print("Exiting")
        # exc_type: тип исключения (None если нет)
        # exc_val: значение исключения
        # exc_tb: traceback
        return False  # Пробросить исключение дальше

with MyContext() as ctx:
    print("Inside")
# Вывод:
# Entering
# Inside
# Exiting

Примеры контекстных менеджеров

1. Файлы:

with open('data.txt') as f:
    lines = f.readlines()

# __exit__ закроет файл автоматически

2. Блокировки (Threading):

import threading

lock = threading.Lock()

with lock:
    # Потокобезопасная операция
    shared_resource.modify()
# Lock автоматически отпущен

3. Контроль времени:

import time
from contextlib import contextmanager

@contextmanager
def timer():
    start = time.time()
    yield
    elapsed = time.time() - start
    print(f"Elapsed: {elapsed:.2f}s")

with timer():
    time.sleep(1)
# Elapsed: 1.00s

4. Подавление исключений:

from contextlib import suppress

with suppress(FileNotFoundError):
    os.remove('file.txt')
# Если файл не существует, ошибка будет проигнорирована

5. Временное изменение состояния:

from contextlib import contextmanager

@contextmanager
def change_cwd(path):
    old_cwd = os.getcwd()
    os.chdir(path)
    try:
        yield
    finally:
        os.chdir(old_cwd)

with change_cwd('/tmp'):
    print(os.getcwd())  # /tmp
print(os.getcwd())  # back to original

Обработка исключений в exit

class ErrorHandler:
    def __enter__(self):
        return self
    
    def __exit__(self, exc_type, exc_val, exc_tb):
        if exc_type is ValueError:
            print(f"Caught ValueError: {exc_val}")
            return True  # Подавить исключение
        elif exc_type is KeyError:
            print(f"Caught KeyError: {exc_val}")
            return True  # Подавить
        return False  # Пробросить дальше

with ErrorHandler():
    raise ValueError("Something bad")  # Будет подавлено

with ErrorHandler():
    raise RuntimeError("Something worse")  # Будет проброшено

@contextmanager декоратор

from contextlib import contextmanager

@contextmanager
def my_context():
    print("Enter")
    try:
        yield "resource"
    finally:
        print("Exit")

with my_context() as resource:
    print(f"Using {resource}")

# Вывод:
# Enter
# Using resource
# Exit

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

from aiofiles import open as aopen

async def read_file():
    async with aopen('file.txt') as f:
        content = await f.read()
    return content

# Магические методы: __aenter__ и __aexit__
class AsyncContext:
    async def __aenter__(self):
        print("Entering async")
        return self
    
    async def __aexit__(self, exc_type, exc_val, exc_tb):
        print("Exiting async")
        return False

async def main():
    async with AsyncContext():
        print("Inside")

Практические примеры

1. Connection pooling:

from contextlib import contextmanager

class Database:
    @contextmanager
    def connection(self):
        conn = self.pool.acquire()
        try:
            yield conn
        finally:
            self.pool.release(conn)

db = Database()
with db.connection() as conn:
    result = conn.execute("SELECT * FROM users")

2. Транзакции:

from contextlib import contextmanager

@contextmanager
def transaction(db):
    db.begin()
    try:
        yield
        db.commit()
    except:
        db.rollback()
        raise

with transaction(db):
    db.insert('users', {'name': 'John'})
    db.insert('users', {'name': 'Jane'})
# Оба инсерта в одной транзакции

3. Временное переопределение:

from unittest.mock import patch

with patch('requests.get') as mock_get:
    mock_get.return_value.status_code = 200
    response = requests.get('https://api.example.com')
    assert response.status_code == 200
# requests.get вернул в нормальное состояние

Преимущества контекстных менеджеров

# Плохо: легко забыть close()
f = open('file.txt')
data = f.read()
# Забыли f.close()!

# Хорошо: гарантированное закрытие
with open('file.txt') as f:
    data = f.read()
# Закроется автоматически, даже если исключение

Вывод: Контекстные менеджеры — это мощный инструмент для управления ресурсами. Ключевые слова with и as упрощают работу с файлами, блокировками и другими ресурсами, гарантируя правильную очистку.

Какие знаешь ключевые слова в контекстном менеджере? | PrepBro