class DatabaseConnection:
def __init__(self, host, database):
self.host = host
self.database = database
self.connection = None
def __enter__(self):
self.connection = self.connect() # Реальная логика подключения
return self.connection
def __exit__(self, exc_type, exc_val, exc_tb):
if self.connection:
self.connection.close() # Реальная логика закрытия соединения
with DatabaseConnection("localhost", "mydb") as db:
# Работа с соединением (db)
cursor = db.cursor()
cursor.execute("SELECT * FROM users")
result = cursor.fetchall()
# ...
# Соединение автоматически закрывается после выхода из блока with
В __enter__
устанавливается соединение, а в __exit__
гарантированно закрывается, даже если возникнет ошибка внутри блока with
. exc_type
, exc_val
и exc_tb
содержат информацию об исключении, если оно произошло.
Контекстные менеджеры в Python – это мощный инструмент для управления ресурсами, гарантирующий их корректное освобождение после использования, даже при возникновении исключений. Для сетевых соединений и баз данных они особенно полезны, поскольку позволяют автоматически закрывать соединения, освобождать ресурсы и обрабатывать транзакции.
Работа с сетевыми соединениями (например, через socket
):
Хотя напрямую socket
не предоставляет готовый контекстный менеджер, его можно легко создать с помощью библиотеки contextlib
и декоратора contextmanager
:
import socket
from contextlib import contextmanager
@contextmanager
def socket_connection(host, port):
"""Контекстный менеджер для работы с сокетом."""
sock = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
try:
sock.connect((host, port))
yield sock
finally:
sock.close()
# Пример использования:
with socket_connection('example.com', 80) as s:
s.sendall(b"GET / HTTP/1.1\r\nHost: example.com\r\nConnection: close\r\n\r\n")
response = s.recv(4096)
print(response.decode())
# Сокет автоматически закрывается после выхода из блока 'with'
В этом примере:
@contextmanager
превращает функцию socket_connection
в контекстный менеджер.yield sock
возвращает объект сокета, который используется внутри блока with
.finally
гарантируется закрытие сокета, даже если в блоке with
возникнет исключение.Работа с базами данных (например, с sqlite3
):
Многие библиотеки для работы с базами данных, включая sqlite3
, предоставляют встроенные контекстные менеджеры:
import sqlite3
db_path = 'mydatabase.db'
with sqlite3.connect(db_path) as conn:
cursor = conn.cursor()
cursor.execute("CREATE TABLE IF NOT EXISTS users (id INTEGER PRIMARY KEY, name TEXT, age INTEGER)")
cursor.execute("INSERT INTO users (name, age) VALUES ('Alice', 30)")
cursor.execute("INSERT INTO users (name, age) VALUES ('Bob', 25)")
conn.commit() # Подтверждаем изменения
for row in cursor.execute("SELECT * FROM users"):
print(row)
# Соединение автоматически закрывается и транзакция коммитится (или откатывается при исключении)
В этом примере:
sqlite3.connect()
возвращает объект соединения, который действует как контекстный менеджер.with
устанавливается соединение с базой данных.conn.commit()
явно подтверждает транзакцию. Если произойдет исключение до вызова conn.commit()
, транзакция будет автоматически отменена при выходе из блока with
. Если conn.commit()
вызван, изменения фиксируются, и соединение закрывается при выходе из блока.with
соединение автоматически закрывается, и, если не было вызвано исключение, транзакция подтверждается. В противном случае транзакция откатывается.Преимущества использования контекстных менеджеров:
Таким образом, контекстные менеджеры – это ценный инструмент для написания надежного и эффективного кода при работе с сетевыми соединениями и базами данных в Python.