Lazy loading with a proxy in Python: defer expensive service creation
A lazy proxy defers creating an expensive service object until its method is first called, then caches it for reuse.
Python code
37 linesimport time
import random
class ExpensiveService:
def __init__(self, name):
self.name = name
print(f"Creating expensive service: {self.name}")
def fetch_data(self):
time.sleep(1)
return f"Data from {self.name}: {random.randint(1, 100)}"
class LazyProxy:
def __init__(self, name):
self._name = name
self._service = None
def get_service(self):
if self._service is None:
self._service = ExpensiveService(self._name)
return self._service
def fetch_data(self):
return self.get_service().fetch_data()
if __name__ == "__main__":
print("Client starts...")
proxy = LazyProxy("mock-service")
print("Proxy created (expensive service NOT yet instantiated)")
for i in range(3):
print(f"Call {i + 1}: {proxy.fetch_data()}")
print("Client ends...")
Output
Client starts...
Proxy created (expensive service NOT yet instantiated)
Creating expensive service: mock-service
Call 1: Data from mock-service: 42
Call 2: Data from mock-service: 37
Call 3: Data from mock-service: 89
Client ends...
How it works
The LazyProxy holds a reference to the real service name but delays constructing the ExpensiveService until fetch_data() is called. Inside get_service(), the proxy checks _service is None and only then creates the instance, printing the creation message just once. After the first call, _service is cached, so subsequent calls reuse the same object — notice the creation message appears only once even though fetch_data() is called three times. This mirrors the Proxy pattern where the proxy controls access to the real object, adding deferred initialization without changing the client interface.
Common mistakes
- Instantiating the service in the proxy's __init__ instead of delaying until first use.
- Forgetting to cache the service, causing a new instance on every call.
- Not handling thread safety when lazy initialization is shared across threads.
Variations
- Use a property on the proxy to lazily initialize with an internal `_get_service` method.
Real-world use cases
- Deferring the creation of heavy database connections until the first query.
- Mocking external API clients in tests to avoid network calls until a method is actually used.
- Optimizing startup time in services by lazily loading expensive resources like ML models.
Sponsored
More from System design patterns
- Build a BFF (Backend for Frontend) Mock Aggregator in Python medium
- Builder pattern for mocking complex objects in Python easy
- Circuit Breaker Pattern in Python: Closed, Open, and Half-Open States medium
- Create a Data Helper Class in Python easy
- Domain Driven Design Aggregate Root Example in Python medium
- Facade Pattern in Python with Mock Simplification medium
Keep learning
Related tutorials and quizzes for this topic.