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

Что такое Singletone?

2.0 Middle🔥 61 комментариев
#Python Core#Soft Skills

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

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

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

Singleton (Одиночка)

Singleton — это порождающий паттерн проектирования, который гарантирует, что у класса есть только один экземпляр (инстанс), и предоставляет глобальную точку доступа к этому экземпляру. Это один из самых распространённых паттернов в программировании, используется для управления общими ресурсами или конфигурацией.

Основная идея

Singleton гарантирует, что во всём приложении существует только один объект класса, независимо от количества попыток создания новых экземпляров. Это полезно для:

  • Управления ресурсами — подключение к БД, логирование
  • Конфигурации — единая конфигурация приложения
  • Кэширования — единый кэш для всего приложения
  • Координации — единый диспетчер событий

Реализация Singleton в Python

1. Классический способ с методом класса

class Database:
    _instance = None
    
    def __new__(cls):
        if cls._instance is None:
            cls._instance = super().__new__(cls)
            cls._instance._initialized = False
        return cls._instance
    
    def __init__(self):
        if self._initialized:
            return
        
        self.connection = None
        self.port = 5432
        self._initialized = True
        print("Database initialized")
    
    def connect(self):
        if self.connection is None:
            self.connection = f"Connected to DB on port {self.port}"
            print(self.connection)
        return self.connection

# Использование
db1 = Database()
db2 = Database()

print(db1 is db2)  # True - один и тот же объект
db1.connect()  # Connected to DB on port 5432
db2.connect()  # Вернёт существующее подключение (не инициализирует заново)

2. Реализация с использованием декоратора

def singleton(cls):
    instances = {}
    
    def get_instance(*args, **kwargs):
        if cls not in instances:
            instances[cls] = cls(*args, **kwargs)
        return instances[cls]
    
    return get_instance

@singleton
class Logger:
    def __init__(self):
        self.logs = []
        print("Logger initialized")
    
    def log(self, message: str):
        self.logs.append(message)
        print(f"[LOG] {message}")
    
    def get_logs(self):
        return self.logs

# Использование
logger1 = Logger()
logger2 = Logger()

logger1.log("First message")
logger2.log("Second message")

print(logger1 is logger2)  # True
print(logger1.get_logs())  # ["First message", "Second message"]

3. Реализация с использованием метакласса

class SingletonMeta(type):
    """Метакласс для создания Singleton классов"""
    _instances = {}
    
    def __call__(cls, *args, **kwargs):
        if cls not in cls._instances:
            instance = super().__call__(*args, **kwargs)
            cls._instances[cls] = instance
        return cls._instances[cls]

class Configuration(metaclass=SingletonMeta):
    def __init__(self):
        self.settings = {}
        print("Configuration initialized")
    
    def set_value(self, key: str, value):
        self.settings[key] = value
    
    def get_value(self, key: str):
        return self.settings.get(key)

# Использование
config1 = Configuration()
config2 = Configuration()

config1.set_value("debug_mode", True)
config2.set_value("timeout", 30)

print(config1 is config2)  # True
print(config1.get_value("debug_mode"))  # True
print(config2.get_value("timeout"))  # 30

4. Thread-safe реализация

Для многопоточных приложений нужна синхронизация:

import threading

class ThreadSafeDatabase:
    _instance = None
    _lock = threading.Lock()
    
    def __new__(cls):
        if cls._instance is None:
            with cls._lock:
                if cls._instance is None:
                    cls._instance = super().__new__(cls)
                    cls._instance._initialized = False
        return cls._instance
    
    def __init__(self):
        if self._initialized:
            return
        
        with self._lock:
            if not self._initialized:
                self.connection = None
                self._initialized = True
                print("Database initialized")
    
    def execute_query(self, query: str):
        print(f"Executing: {query}")

# Использование в многопоточной среде
def create_connection():
    db = ThreadSafeDatabase()
    db.execute_query("SELECT * FROM users")

threads = [threading.Thread(target=create_connection) for _ in range(5)]
for thread in threads:
    thread.start()
for thread in threads:
    thread.join()

5. Модульный Singleton (Pythonic способ)

В Python часто используется модульный уровень вместо класса:

# database.py
class _Database:
    def __init__(self):
        self.connection = None
        print("Database module loaded")
    
    def connect(self):
        if self.connection is None:
            self.connection = "Connected"
            print("Database connected")
        return self.connection

# Создаём единственный экземпляр на уровне модуля
database = _Database()

# main.py
from database import database

db1 = database  # Получаем singleton
db2 = database  # Тот же объект

print(db1 is db2)  # True
db1.connect()  # Database connected

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

Система логирования

import threading
from datetime import datetime

class Logger:
    _instance = None
    _lock = threading.Lock()
    
    def __new__(cls):
        if cls._instance is None:
            with cls._lock:
                if cls._instance is None:
                    cls._instance = super().__new__(cls)
                    cls._instance._initialized = False
        return cls._instance
    
    def __init__(self):
        if self._initialized:
            return
        
        self._initialized = True
        self.logs = []
    
    def info(self, message: str):
        log_entry = f"[{datetime.now().isoformat()}] INFO: {message}"
        self.logs.append(log_entry)
        print(log_entry)
    
    def error(self, message: str):
        log_entry = f"[{datetime.now().isoformat()}] ERROR: {message}"
        self.logs.append(log_entry)
        print(log_entry)

# Использование
logger = Logger()
logger.info("Application started")
logger.error("Connection failed")

Управление конфигурацией

from typing import Any, Optional

class Config:
    _instance = None
    
    def __new__(cls):
        if cls._instance is None:
            cls._instance = super().__new__(cls)
            cls._instance._initialized = False
        return cls._instance
    
    def __init__(self):
        if self._initialized:
            return
        
        self._initialized = True
        self.settings = {}
    
    def load_from_dict(self, data: dict):
        self.settings.update(data)
    
    def get(self, key: str, default: Any = None) -> Any:
        return self.settings.get(key, default)
    
    def set(self, key: str, value: Any):
        self.settings[key] = value

# Использование
config = Config()
config.load_from_dict({
    "database_url": "postgresql://localhost/mydb",
    "debug": True,
    "secret_key": "my-secret-key"
})

db_url = config.get("database_url")
print(db_url)  # postgresql://localhost/mydb

Недостатки и когда избегать Singleton

  • Сложность тестирования — трудно мокировать и тестировать
  • Скрытые зависимости — глобальное состояние затемняет зависимости
  • Проблемы многопоточности — требует синхронизации
  • Нарушение принципа SRP — класс отвечает и за логику, и за управление инстансом

Альтернативы

# Вместо Singleton лучше использовать внедрение зависимостей
class Service:
    def __init__(self, logger):
        self.logger = logger
    
    def do_work(self):
        self.logger.info("Working...")

# Или использовать фабрику
class LoggerFactory:
    @staticmethod
    def create_logger():
        return Logger()

Итоги

Singleton — мощный паттерн для управления единственным экземпляром критичного ресурса. В Python его чаще всего реализуют через декораторы или метаклассы. Однако в современном коде часто предпочитают внедрение зависимостей вместо глобальных Singleton объектов.

Что такое Singletone? | PrepBro