📜 Старый подход: ручное управление ресурсами
import pymysql
connection = pymysql.connect(host="localhost", user="root", password="secret", database="shop")
cursor = connection.cursor()
cursor.execute("SELECT * FROM products")
for row in cursor.fetchall():
print(row)
cursor.close()
connection.close()
Что здесь происходит: код работает, но если между строками возникнет исключение, cursor.close() и connection.close() могут не выполниться. Это приводит к «утечке» открытых соединений.
❌ Почему ручной подход не лучший
- Утечка ресурсов: при ошибке соединение может остаться открытым.
- Дублирование: приходится везде писать
cursor.close()иconnection.close(). - Код на верхнем уровне: при импорте модуля запросы выполняются немедленно, что неожиданно для пользователя модуля.
✅ Современный вариант: with и точка входа
import pymysql
def get_products():
with pymysql.connect(
host="localhost",
user="root",
password="secret",
database="shop"
) as connection:
with connection.cursor() as cursor:
cursor.execute("SELECT * FROM products")
return cursor.fetchall()
if __name__ == "__main__":
for product in get_products():
print(product)
Что улучшилось:
- Контекстный менеджер
withавтоматически закрывает курсор и соединение, даже если произошла ошибка. - Логика работы с базой вынесена в функцию, которую легко протестировать и переиспользовать.
- Блок
if __name__ == "__main__":гарантирует, что демонстрационный код запустится только при прямом запуске файла.
✅ Современный вариант для MongoDB
from pymongo import MongoClient
def update_prices(collection):
collection.delete_many({})
collection.insert_many([
{"name": "Pen", "price": 1.50},
{"name": "Notebook", "price": 3.99},
])
result = collection.update_many({}, {"$mul": {"price": 1.2}})
return result.modified_count
if __name__ == "__main__":
with MongoClient("mongodb://localhost:27017") as client:
db = client["ich_edit"]
collection = db["products_pd_ivanov"]
count = update_prices(collection)
print(f"Updated {count} products")
Что улучшилось: MongoClient тоже поддерживает контекстный менеджер и сам закроет пул соединений при выходе из блока with.
🕰️ Когда старый подход ещё можно встретить
В старых учебниках и legacy-проектах вы можете увидеть ручные вызовы close(). Если попадается такой код, проверяйте:
- Есть ли гарантия закрытия ресурсов при исключении?
- Не выполняется ли код на уровне модуля при импорте?
При рефакторинге лучше переписать такие участки на with и выделить точку входа if __name__ == "__main__":.