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.
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.
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.
How this landed with the community
Was this worth your time?
0 Comments
Thoughtful readers leave field notes, pushback, and hard-won operational detail here.