Singleton Pattern
Part of the Object Oriented Programming section of Coddy's Python journey — lesson 45 of 64.
The Singleton pattern ensures a class has only one instance and provides a global point of access to it. This is useful for resources like database connections or configuration settings.
Here is a basic Singleton implementation:
class Singleton:
_instance = None
def __new__(cls):
if cls._instance is None:
cls._instance = super().__new__(cls)
return cls._instanceThe __new__ method controls object creation. It checks if an instance already exists before creating a new one.
Create two instances of the Singleton class:
singleton1 = Singleton()
singleton2 = Singleton()Check if both variables reference the same object:
print(singleton1 is singleton2) # True
print(id(singleton1)) # Same memory address
print(id(singleton2)) # Same memory addressHere is a more practical example with a database connection:
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}"
# First access creates the connection
db1 = DatabaseConnection()
print(db1.connection)
# Second access reuses the same connection
db2 = DatabaseConnection()
print(db2.connection)
print(db1.query("SELECT * FROM users"))
print(db1 is db2)Create a configuration manager using 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 - same settingsOutput:
True
140234567890123
140234567890123
Creating new database connection
Connected to MySQL database
Connected to MySQL database
Executing: SELECT * FROM users
True
TrueKey Point: The Singleton pattern uses __new__ to control object creation, ensuring only one instance exists. Use it for resources that should have only one copy throughout your application, like database connections, loggers, or configuration managers. Remember that all variables pointing to a Singleton reference the same object in memory.
Challenge
EasyIn this challenge, you'll implement the Singleton design pattern for a database connection class.
- Edit
databaseconnection.pyto implement the Singleton pattern following the TODO comments - The
driver.pyfile contains extensive test scenarios and should not be modified
Cheat sheet
The Singleton pattern ensures a class has only one instance and provides a global point of access to it.
Basic Singleton implementation using __new__:
class Singleton:
_instance = None
def __new__(cls):
if cls._instance is None:
cls._instance = super().__new__(cls)
return cls._instanceThe __new__ method controls object creation by checking if an instance already exists before creating a new one.
Testing Singleton instances:
singleton1 = Singleton()
singleton2 = Singleton()
print(singleton1 is singleton2) # True - same objectPractical example with database connection:
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}"Configuration manager example:
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)Use Singleton for resources that should have only one copy throughout your application, like database connections, loggers, or configuration managers.
Try it yourself
from databaseconnection import DatabaseConnection
# Comprehensive test case handler
test_case = input()
if test_case == "identity_check":
db1 = DatabaseConnection()
db2 = DatabaseConnection()
print(db1 is db2) # Should print True
elif test_case == "connect_state":
db = DatabaseConnection()
print(f"Initial connected state: {db.connected}") # Should print False
db.connect()
print(f"After connect: {db.connected}") # Should print True
elif test_case == "disconnect_state":
db = DatabaseConnection()
db.connect()
db.disconnect()
print(f"After disconnect: {db.connected}") # Should print False
elif test_case == "multiple_instances_same_state":
db1 = DatabaseConnection()
db1.connect()
db2 = DatabaseConnection()
print(f"db2 connected state: {db2.connected}") # Should print True
elif test_case == "host_value":
db = DatabaseConnection()
print(f"Default host: {db.host}") # Should print "localhost"
db.host = "new-server"
db2 = DatabaseConnection()
print(f"New instance host: {db2.host}") # Should print "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}") # Should print True
print(f"Host value: {db1.host}") # Should print "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')}") # Should print True
print(f"db2 port value: {db2.port}") # Should print 3306
elif test_case == "reset_connection":
db1 = DatabaseConnection()
db1.connect()
print(f"Connected state: {db1.connected}") # Should print True
db2 = DatabaseConnection()
print(f"New instance connected state: {db2.connected}") # Should print True
db2.disconnect()
db3 = DatabaseConnection()
print(f"After disconnect, new instance state: {db3.connected}") # Should print False
elif test_case == "multiple_connects":
db = DatabaseConnection()
db.connect()
db.connect()
db.connect()
print(f"Connected state after multiple connects: {db.connected}") # Should print True
db.disconnect()
print(f"Connected state after disconnect: {db.connected}") # Should print False
elif test_case == "host_change_affects_message":
db = DatabaseConnection()
db.host = "custom-server"
db.connect() # Will still print "Connected to database at localhost"
# The connect method has a hardcoded message that doesn't use the host attribute
print("Message still shows localhost: True")This lesson includes a short quiz. Start the lesson to answer it and track your progress.
All lessons in Object Oriented Programming
1Fundamentals of OOP
External FilesIntroduction to OOPClasses vs ObjectsThe self ParameterMethodsAttributesConstructor Method (__init__)Recap - Simple Calculator4Inheritance
Basic InheritanceThe super() FunctionMethod OverridingMultiple InheritanceMethod Resolution OrderRecap - Employee Hierarchy7Special Methods
Magic Methods IntroductionOperator OverloadingContainer Magic MethodsRecap - Custom List10Design Patterns Part 1
Intro to design patternSingleton PatternFactory PatternObserver PatternStrategy Pattern2Decorators
Introduction to DecoratorsProperty DecoratorStatic Method DecoratorClass Method Decorator5Polymorphism
Method Overriding RevisitedDuck TypingAbstract Classes and MethodsInterface DesignRecap - Shape Calculator8Advanced OOP Concepts
Composition vs InheritanceMixinsStatic and Class MethodsClass DecoratorsContext Managers3Class Properties
Instance vs Class VariablesProperty DecoratorsPrivate AttributesRecap - Bank Account Manager6Encapsulation
Public, Protected, Private MemAccess ModifiersInformation HidingProperty Decorators AdvancedRecap - Student Records System12Project: Library Management
Project OverviewBook and User Classes