How to Lazy Load an Expensive Attribute with a Proxy in Python
This code shows a Proxy class that lazily loads an ExpensiveResource only when first accessed, caching it for subsequent uses.
Python code
27 linesclass ExpensiveResource:
def __init__(self, name):
self.name = name
print(f"Expensive resource '{name}' created (e.g., DB connection)")
def use(self):
return f"Using {self.name}"
class Proxy:
def __init__(self, name):
self._name = name
self._resource = None
@property
def resource(self):
if self._resource is None:
self._resource = ExpensiveResource(self._name)
return self._resource
def use(self):
return self.resource.use()
if __name__ == "__main__":
proxy = Proxy("database")
print("Proxy created; resource not loaded yet")
print(proxy.use()) # Triggers lazy load
print(proxy.use()) # Reuses cached resource
Output
Proxy created; resource not loaded yet
Expensive resource 'database' created (e.g., DB connection)
Using database
Using database
How it works
The resource property uses a check-then-create pattern: if _resource is None, it creates the expensive object once; otherwise it returns the existing one. This defers costly initialization until the attribute is actually needed, improving startup time. The use() method delegates to the resource, so callers only interact with the proxy and never see the lazy-loading logic. This is a classic example of the Proxy design pattern combined with lazy initialization.
Common mistakes
- Forgetting to store the result in `self._resource` after creation, causing it to be recreated every access.
- Not using a separate sentinel value when `None` could be a valid resource, leading to repeated loading.
- Assuming the property is called only once without verifying it's cached, leading to performance issues.
- Making the resource attribute public, allowing direct assignment that bypasses lazy-loading.
Variations
- Use `functools.cached_property` for a read-only lazy-loaded attribute.
- Use a `__getattr__` method to lazily load attributes when accessed.
Real-world use cases
- Deferring the creation of a database connection pool until the first query, reducing startup latency in web applications.
- Loading a large configuration file or heavy ML model only when a specific feature is first used.
- Caching the result of an expensive remote API call per object instance for repeated access.
Sponsored
More from OOP & classes
- Add property getter setter validation in Python easy
- Binary Tree Inorder Traversal in Python easy
- Borg pattern shared state in Python medium
- Bridge Pattern in Python: Separate Abstraction from Implementation medium
- Composable Predicates with the &, |, ~ Operators in Python medium
- Composition over Inheritance: How to Build a Wallet Account in Python easy
Keep learning
Related tutorials and quizzes for this topic.