Паттерн Singleton
Часть раздела Object Oriented Programming путешествия по Python на Coddy — урок 45 из 64.
Паттерн Singleton гарантирует, что у класса есть только один экземпляр, и предоставляет глобальную точку доступа к нему. Это полезно для таких ресурсов, как подключения к базе данных или настройки конфигурации.
Вот базовая реализация Singleton:
class Singleton:
_instance = None
def __new__(cls):
if cls._instance is None:
cls._instance = super().__new__(cls)
return cls._instanceМетод __new__ управляет созданием объектов. Он проверяет, существует ли уже экземпляр, прежде чем создавать новый.
Создайте два экземпляра класса 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__ для управления созданием объектов, гарантируя существование только одного экземпляра. Используйте его для ресурсов, которые должны иметь только одну копию во всем приложении, таких как подключения к базе данных, логгеры или менеджеры конфигурации. Помните, что все переменные, указывающие на Singleton, ссылаются на один и тот же объект в памяти.
Задание
ЛегкоВ этом испытании вы реализуете паттерн проектирования Одиночка (Singleton) для класса подключения к базе данных.
- Отредактируйте
databaseconnection.py, чтобы реализовать паттерн Одиночка, следуя комментариям TODO - Файл
driver.pyсодержит обширные тестовые сценарии и не должен изменяться
Шпаргалка
Паттерн Одиночка (Singleton) гарантирует, что у класса есть только один экземпляр, и предоставляет глобальную точку доступа к нему.
Базовая реализация Одиночки с использованием __new__:
class Singleton:
_instance = None
def __new__(cls):
if cls._instance is None:
cls._instance = super().__new__(cls)
return cls._instanceМетод __new__ управляет созданием объекта, проверяя, существует ли уже экземпляр, прежде чем создавать новый.
Тестирование экземпляров Одиночки:
singleton1 = Singleton()
singleton2 = Singleton()
print(singleton1 is singleton2) # True - тот же объектПрактический пример с подключением к базе данных:
class DatabaseConnection:
_instance = None
def __new__(cls):
if cls._instance is None:
cls._instance = super().__new__(cls)
cls._instance.connection = "Connected to MySQL database"
return cls._instance
def query(self, sql):
return f"Executing: {sql}"Пример менеджера конфигурации:
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)Используйте Одиночку для ресурсов, которые должны иметь только одну копию во всем приложении, таких как подключения к базе данных, логгеры или менеджеры конфигурации.
Попробуйте сами
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() # Должно вывести "Connected to database at localhost"
elif test_case == "disconnect_message":
db = DatabaseConnection()
db.connect()
db.disconnect() # Должно вывести "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() # Все равно выведет "Connected to database at localhost"
# Метод connect имеет жестко заданное сообщение, которое не использует атрибут host
print("Message still shows localhost: True")В этом уроке есть небольшой тест. Начните урок, чтобы ответить на вопросы и сохранить прогресс.
Все уроки раздела Object Oriented Programming
1Основы ООП
Внешние файлыВведение в ООПКлассы и объектыПараметр selfМетодыАтрибутыМетод-конструктор (__init__)Повторение — Простой калькулятор4Наследование
Базовое наследованиеФункция super()Переопределение методовМножественное наследованиеПорядок разрешения методовИтоги — Иерархия сотрудников7Специальные методы
Введение в магические методыПерегрузка операторовМагические методы контейнеровПовторение — Пользовательский список10Паттерны проектирования. Часть 1
Введение в паттерны проектированияПаттерн SingletonПаттерн FactoryПаттерн ObserverПаттерн Strategy5Полиморфизм
Снова о переопределении методовУтиная типизацияАбстрактные классы и методыПроектирование интерфейсовИтоги — Калькулятор фигур8Продвинутые концепции ООП
Композиция против наследованияМиксиныСтатические методы и методы классаДекораторы классовМенеджеры контекста11Паттерны проектирования. Часть 2
Паттерн КомандаПаттерн АдаптерПаттерн ДекораторПаттерн Шаблонный методПаттерн СостояниеПаттерн Компоновщик3Свойства классов
Переменные экземпляра и классаДекораторы свойствПриватные атрибутыИтоги — Менеджер банковского счета6Инкапсуляция
Public, Protected, Private члены классаМодификаторы доступаСокрытие данныхДекораторы property: продвинутый уровеньИтоги: Система учета студентов12Проект: Управление библиотекой
Обзор проектаКлассы Book и User