Extension: Singleton Pattern
A design pattern is a reusable solution to a common programming problem. The Singleton pattern ensures a class has exactly one instanceand provides a global point of access to it.
When to Use a Singleton
- Application configuration (one config object for the whole app)
- Database connection pool (one shared connection manager)
- Logging service (one log file writer)
- Hardware interfaces (one printer spooler, one GPU context)
The Problem Without Singleton
config1 = Config()
config2 = Config()
config1.debug = True
print(config2.debug) # → False — different object!Without Singleton, you can accidentally create multiple independent configs that fall out of sync.
Implementing with __new__
Python calls __new__ to create an object (before __init__initialises it). By overriding __new__, we intercept the creation step.
class Config:
_instance = None
def __new__(cls):
if cls._instance is None:
cls._instance = super().__new__(cls)
return cls._instance
a = Config(); b = Config()
print(a is b) # → TrueTrueOne More Gotcha: __init__ Still Runs
Even if __new__ returns the same object each time, Python still calls __init__ on every Config(). Without a guard, later calls can reset shared state back to default values.
class Config:
_instance = None
def __new__(cls):
if cls._instance is None:
cls._instance = super().__new__(cls)
return cls._instance
def __init__(self):
if hasattr(self, "_initialized"):
return
self.debug = False
self.version = "1.0"
self._initialized = TrueIdentity vs Equality
a == b— are they equal? (can be overridden with__eq__)a is b— are they the same object in memory? (identity)- For Singleton,
a is bmust beTrue
Your Task
- Add
_instance = NonetoConfig - Fix
__new__so it creates the object only once and always returns that same object - Stop later calls to
Config()from resettingdebugandversion
After your fix, a is b and b is c should both be True, and c.debug should still be Trueafter you set a.debug = True.
When to Use a Singleton
Explore this concept
Extension: Singleton Pattern
A design pattern is a reusable solution to a common programming problem. The Singleton pattern ensures a class has exactly one instanceand provides a global point of access to it.
When to Use a Singleton
- Application configuration (one config object for the whole app)
- Database connection pool (one shared connection manager)
- Logging service (one log file writer)
- Hardware interfaces (one printer spooler, one GPU context)
The Problem Without Singleton
config1 = Config()
config2 = Config()
config1.debug = True
print(config2.debug) # → False — different object!Without Singleton, you can accidentally create multiple independent configs that fall out of sync.
Implementing with __new__
Python calls __new__ to create an object (before __init__initialises it). By overriding __new__, we intercept the creation step.
class Config:
_instance = None
def __new__(cls):
if cls._instance is None:
cls._instance = super().__new__(cls)
return cls._instance
a = Config(); b = Config()
print(a is b) # → TrueTrueOne More Gotcha: __init__ Still Runs
Even if __new__ returns the same object each time, Python still calls __init__ on every Config(). Without a guard, later calls can reset shared state back to default values.
class Config:
_instance = None
def __new__(cls):
if cls._instance is None:
cls._instance = super().__new__(cls)
return cls._instance
def __init__(self):
if hasattr(self, "_initialized"):
return
self.debug = False
self.version = "1.0"
self._initialized = TrueIdentity vs Equality
a == b— are they equal? (can be overridden with__eq__)a is b— are they the same object in memory? (identity)- For Singleton,
a is bmust beTrue
Your Task
- Add
_instance = NonetoConfig - Fix
__new__so it creates the object only once and always returns that same object - Stop later calls to
Config()from resettingdebugandversion
After your fix, a is b and b is c should both be True, and c.debug should still be Trueafter you set a.debug = True.