Fundamentals 5 min read

Implementing Lazy Properties in Python with Descriptors and @property

This article explains the concept of lazy initialization in Python, reviews the @property decorator, and provides two complete implementations—a descriptor class and a decorator function—to create lazy properties that compute a value only once and then cache it for subsequent accesses.

Python Programming Learning Circle
Python Programming Learning Circle
Python Programming Learning Circle
Implementing Lazy Properties in Python with Descriptors and @property

Lazy initialization in Python means that a value is computed only the first time it is needed and then stored for future use, which can improve performance and reduce memory consumption.

The built‑in @property decorator turns method calls into attribute accesses, but each access still recomputes the value.

To avoid repeated calculations, a lazy property can be created using either a custom descriptor or a decorator that caches the result after the first evaluation.

Method 1 – Descriptor class

<code>class lazy(object):
    def __init__(self, func):
        self.func = func
    def __get__(self, instance, cls):
        val = self.func(instance)
        setattr(instance, self.func.__name__, val)
        return val

class Circle(object):
    def __init__(self, radius):
        self.radius = radius
    @lazy
    def area(self):
        print('evaluate')
        return 3.14 * self.radius ** 2

c = Circle(4)
print(c.radius)
print(c.area)   # first call prints 'evaluate' and computes
print(c.area)   # subsequent calls use cached value
</code>

The descriptor’s __get__ method computes the value, stores it as an attribute on the instance, and returns it; later accesses retrieve the cached attribute directly.

Method 2 – Decorator function

<code>def lazy_property(func):
    attr_name = "_lazy_" + func.__name__
    @property
    def _lazy_property(self):
        if not hasattr(self, attr_name):
            setattr(self, attr_name, func(self))
        return getattr(self, attr_name)
    return _lazy_property

class Circle(object):
    def __init__(self, radius):
        self.radius = radius
    @lazy_property
    def area(self):
        print('evaluate')
        return 3.14 * self.radius ** 2

c = Circle(4)
print(c.radius)
print(c.area)   # first call prints 'evaluate'
print(c.area)   # subsequent calls use cached value
</code>

This decorator creates a hidden attribute to store the computed result; the first call evaluates the function and saves the value, while later calls simply return the stored result.

Both approaches achieve the same effect: the expensive computation runs only once, and the result is reused, improving performance for properties that are costly to calculate.

performancePythonTutorialpropertydescriptorlazy property
Python Programming Learning Circle
Written by

Python Programming Learning Circle

A global community of Chinese Python developers offering technical articles, columns, original video tutorials, and problem sets. Topics include web full‑stack development, web scraping, data analysis, natural language processing, image processing, machine learning, automated testing, DevOps automation, and big data.

0 followers
Reader feedback

How this landed with the community

login Sign in to like

Rate this article

Was this worth your time?

Sign in to rate
Discussion

0 Comments

Thoughtful readers leave field notes, pushback, and hard-won operational detail here.