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.

Medium Python 3.9+ Aug 9, 2026 OOP & classes 15 views 0 copies

Python code

27 lines
Python 3.9+
class 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

stdout
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

  1. Use `functools.cached_property` for a read-only lazy-loaded attribute.
  2. 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

Run this sample

Open the browser IDE to tweak the example and see results without installing anything.

Open editor

More from OOP & classes

Related tutorials and quizzes for this topic.