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

Как работает метод close?

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

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

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

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

Метод close() в Python

Метод close() — это фундаментальный механизм управления ресурсами в Python. Он вызывается для освобождения ресурсов, занятых объектом: закрытие файлов, соединений с БД, сетевых сокетов и прочего.

Где используется close()

1. Файлы

# Плохо: забыть закрыть файл
f = open('data.txt', 'r')
data = f.read()
# Если тут ошибка — файл останется открытым

# Хорошо: явно закрыть
f = open('data.txt', 'r')
try:
    data = f.read()
finally:
    f.close()  # Гарантирует закрытие

# Лучше: использовать context manager
with open('data.txt', 'r') as f:
    data = f.read()
# Файл автоматически закроется при выходе из блока

2. Соединения с БД

import sqlite3

# Плохо
conn = sqlite3.connect(':memory:')
cursor = conn.cursor()
cursor.execute('SELECT * FROM users')
# conn.close() забыли!

# Хорошо
conn = sqlite3.connect(':memory:')
try:
    cursor = conn.cursor()
    cursor.execute('SELECT * FROM users')
finally:
    cursor.close()
    conn.close()

# Лучше
with sqlite3.connect(':memory:') as conn:
    cursor = conn.cursor()
    cursor.execute('SELECT * FROM users')
# Автоматически закроется

3. HTTP сеансы

import requests

# Плохо: утечка соединений
response = requests.get('https://api.example.com/data')
data = response.json()
# Соединение может остаться открытым

# Хорошо
response = requests.get('https://api.example.com/data')
try:
    data = response.json()
finally:
    response.close()

# Лучше
with requests.Session() as session:
    response = session.get('https://api.example.com/data')
    data = response.json()
# Автоматическое закрытие

Как работает close() внутри

1. Context Manager протокол

Объект с методом close() должен реализовать протокол контекстного менеджера:

class FileWrapper:
    def __init__(self, filename):
        self.filename = filename
        self.file = open(filename, 'r')
    
    def __enter__(self):
        """Вызывается при входе в блок with"""
        print(f"Открываю файл {self.filename}")
        return self
    
    def __exit__(self, exc_type, exc_val, exc_tb):
        """Вызывается при выходе из блока with (всегда)"""
        print(f"Закрываю файл {self.filename}")
        self.close()
        return False  # Не подавляем исключение
    
    def close(self):
        """Освобождение ресурсов"""
        if self.file and not self.file.closed:
            self.file.close()
    
    def read(self):
        return self.file.read()

# Использование
with FileWrapper('data.txt') as fw:
    data = fw.read()
# Выводит: Открываю файл..., затем Закрываю файл...

2. Процесс закрытия

Когда вызывается close(), объект:

  1. Прекращает операции — перестаёт принимать новые запросы
  2. Освобождает ресурсы — закрывает файловые дескрипторы, соединения
  3. Выполняет cleanup — сбрасывает кэши, логирует закрытие
  4. Помечает как закрытый — устанавливает флаг closed = True
class DatabaseConnection:
    def __init__(self, host, port):
        self.host = host
        self.port = port
        self.socket = socket.socket()
        self.socket.connect((host, port))
        self.closed = False
    
    def close(self):
        """Закрыть соединение"""
        if self.closed:
            return  # Уже закрыто, ничего не делаем
        
        try:
            # 1. Отправить команду закрытия серверу
            self.socket.sendall(b'CLOSE\r\n')
            
            # 2. Получить подтверждение
            response = self.socket.recv(1024)
            
            # 3. Закрыть сокет
            self.socket.close()
        
        except Exception as e:
            print(f"Ошибка при закрытии: {e}")
        
        finally:
            # 4. Пометить как закрытое (обязательно)
            self.closed = True
    
    def execute(self, query):
        if self.closed:
            raise ConnectionError("Connection is closed")
        # выполнить запрос

Гарантированное закрытие

Context Manager (Рекомендуется)

# Гарантирует close() даже при исключении
with open('file.txt') as f:
    # Если тут ошибка
    data = f.read()
    1 / 0  # ZeroDivisionError
# close() всё равно будет вызван!

Try-Finally

f = open('file.txt')
try:
    data = f.read()
finally:
    f.close()  # Выполнится в любом случае

Contextlib

from contextlib import closing

def get_connection():
    return MyConnection()

with closing(get_connection()) as conn:
    data = conn.execute('SELECT * FROM users')
# close() вызовется автоматически

Идемпотентность close()

Вызов close() дважды должен быть безопасным:

f = open('file.txt')
f.close()
f.close()  # Не должно ошибки, close() идемпотентна
print(f.closed)  # True

Корректная реализация:

class SafeCloseable:
    def __init__(self):
        self.closed = False
    
    def close(self):
        if not self.closed:  # Проверяем перед закрытием
            # Логика закрытия
            self.closed = True

Ошибки при close()

# Ошибка 1: файловый дескриптор утекает
f = open('file.txt')
data = f.read()
# забыли f.close()

# Ошибка 2: операция с закрытым файлом
f = open('file.txt')
f.close()
data = f.read()  # ValueError: I/O operation on closed file

# Ошибка 3: не закрыли соединение в цикле
for url in urls:
    response = requests.get(url)
    # response.close() забыли!
# Истощение соединений (too many open files)

Пример: Реализация close() для кастомного класса

class DataProcessor:
    def __init__(self, cache_size=1000):
        self.cache = {}
        self.cache_size = cache_size
        self.file_handle = None
        self.closed = False
    
    def __enter__(self):
        return self
    
    def __exit__(self, exc_type, exc_val, exc_tb):
        self.close()
    
    def process(self, data):
        if self.closed:
            raise RuntimeError("Processor is closed")
        # обработка
        return data.upper()
    
    def close(self):
        """Освобождение всех ресурсов"""
        if self.closed:
            return
        
        # 1. Очистить кэш
        self.cache.clear()
        
        # 2. Закрыть файл если открыт
        if self.file_handle:
            self.file_handle.close()
        
        # 3. Отметить как закрытое
        self.closed = True
        
        print("DataProcessor закрыт")

# Использование
with DataProcessor() as proc:
    result = proc.process("hello")
# close() вызовется автоматически

Резюме

  • close() освобождает ресурсы объекта
  • Context manager (with) — предпочтительный способ
  • Идемпотентность — close() должна быть безопасна при множественном вызове
  • Try-finally — альтернатива, если context manager недоступен
  • Всегда закрывай ресурсы, иначе утечки памяти и исчерпание лимитов
Как работает метод close? | PrepBro