Menu
Coddy logo textTech

Паттерн Singleton

Часть раздела Object Oriented Programming путешествия по Python на Coddy — урок 45 из 64.

Паттерн Singleton гарантирует, что class имеет только один instance и предоставляет глобальную точку access к нему. Это полезно для таких ресурсов, как подключения к database или settings конфигурации.

Вот базовая реализация паттерна Singleton:

class Singleton:
    _instance = None
    
    def __new__(cls):
        if cls._instance is None:
            cls._instance = super().__new__(cls)
        return cls._instance

Метод __new__ управляет созданием объектов. Он проверяет, существует ли уже instance, перед созданием нового.

Создайте два экземпляра класса Singleton:

singleton1 = Singleton()
singleton2 = Singleton()

Проверьте, ссылаются ли обе переменные на один и тот же объект:

print(singleton1 is singleton2)  # True
print(id(singleton1))            # Тот же адрес памяти
print(id(singleton2))            # Тот же адрес памяти

Вот более практический пример с подключением к базе данных:

class DatabaseConnection:
    _instance = None
    
    def __new__(cls):
        if cls._instance is None:
            cls._instance = super().__new__(cls)
            cls._instance.connection = "Connected to MySQL database"
            print("Creating new database connection")
        return cls._instance
    
    def query(self, sql):
        return f"Executing: {sql}"

# Первый доступ создаёт соединение
db1 = DatabaseConnection()
print(db1.connection)

# Второй доступ повторно использует то же соединение
db2 = DatabaseConnection()
print(db2.connection)

print(db1.query("SELECT * FROM users"))
print(db1 is db2)

Создайте менеджер конфигурации с использованием шаблона Singleton:

class Config:
    _instance = None
    
    def __new__(cls):
        if cls._instance is None:
            cls._instance = super().__new__(cls)
            cls._instance.settings = {}
        return cls._instance
    
    def set_setting(self, key, value):
        self.settings[key] = value
    
    def get_setting(self, key):
        return self.settings.get(key)

config1 = Config()
config1.set_setting("debug", True)

config2 = Config()
print(config2.get_setting("debug"))  # True - те же настройки

Вывод:

True
140234567890123
140234567890123
Creating new database connection
Connected to MySQL database
Connected to MySQL database
Executing: SELECT * FROM users
True
True

Ключевой момент: Паттерн Singleton использует __new__ для управления созданием object, гарантируя, что существует only one instance. Используйте его для ресурсов, которые должны have only one копию во всем приложении, таких как database connection, логгеры или менеджеры конфигурации. Помните, что все переменные, указывающие на Singleton, ссылаются на same object в memory.

challenge icon

Задание

Легко

В этом задании вам предстоит реализовать паттерн проектирования Singleton для класса подключения к базе данных.

  • Отредактируйте файл databaseconnection.py, чтобы реализовать паттерн Singleton в соответствии с комментариями TODO
  • Файл driver.py содержит развернутые тестовые сценарии и не должен изменяться

Попробуйте сами

from databaseconnection import DatabaseConnection

# Комплексный обработчик тестовых случаев
test_case = input()

if test_case == "identity_check":
    db1 = DatabaseConnection()
    db2 = DatabaseConnection()
    print(db1 is db2)  # Должно вывести True

elif test_case == "connect_state":
    db = DatabaseConnection()
    print(f"Initial connected state: {db.connected}")  # Должно вывести False
    db.connect()
    print(f"After connect: {db.connected}")  # Должно вывести True

elif test_case == "disconnect_state":
    db = DatabaseConnection()
    db.connect()
    db.disconnect()
    print(f"After disconnect: {db.connected}")  # Должно вывести False

elif test_case == "multiple_instances_same_state":
    db1 = DatabaseConnection()
    db1.connect()
    db2 = DatabaseConnection()
    print(f"db2 connected state: {db2.connected}")  # Должно вывести True

elif test_case == "host_value":
    db = DatabaseConnection()
    print(f"Default host: {db.host}")  # Должно вывести "localhost"
    db.host = "new-server"
    db2 = DatabaseConnection()
    print(f"New instance host: {db2.host}")  # Должно вывести "new-server"

elif test_case == "init_once":
    db1 = DatabaseConnection()
    db1.host = "custom-host"
    db2 = DatabaseConnection()
    print(f"Both instances have same host: {db1.host == db2.host}")  # Должно вывести True
    print(f"Host value: {db1.host}")  # Должно вывести "custom-host"

elif test_case == "connect_message":
    db = DatabaseConnection()
    db.connect()  # Should print "Connected to database at localhost"

elif test_case == "disconnect_message":
    db = DatabaseConnection()
    db.connect()
    db.disconnect()  # Should print "Disconnected from database"

elif test_case == "attribute_modification":
    db1 = DatabaseConnection()
    db1.port = 3306
    db2 = DatabaseConnection()
    print(f"db2 has port attribute: {hasattr(db2, 'port')}")  # Должно вывести True
    print(f"db2 port value: {db2.port}")  # Должно вывести 3306

elif test_case == "reset_connection":
    db1 = DatabaseConnection()
    db1.connect()
    print(f"Connected state: {db1.connected}")  # Должно вывести True
    db2 = DatabaseConnection()
    print(f"New instance connected state: {db2.connected}")  # Должно вывести True
    db2.disconnect()
    db3 = DatabaseConnection()
    print(f"After disconnect, new instance state: {db3.connected}")  # Должно вывести False

elif test_case == "multiple_connects":
    db = DatabaseConnection()
    db.connect()
    db.connect()
    db.connect()
    print(f"Connected state after multiple connects: {db.connected}")  # Должно вывести True
    db.disconnect()
    print(f"Connected state after disconnect: {db.connected}")  # Должно вывести False

elif test_case == "host_change_affects_message":
    db = DatabaseConnection()
    db.host = "custom-server"
    db.connect()  # Will still print "Connected to database at localhost"
    # Метод connect имеет жёстко заданное сообщение, которое не использует атрибут host
    print("Message still shows localhost: True")
quiz iconПроверьте себя

В этом уроке есть небольшой тест. Начните урок, чтобы ответить на вопросы и сохранить прогресс.

Все уроки раздела Object Oriented Programming