Skip to content

Instantly share code, notes, and snippets.

@mzipay
Created March 28, 2015 06:48
Show Gist options
  • Star 0 You must be signed in to star a gist
  • Fork 0 You must be signed in to fork a gist
  • Save mzipay/ea4f89e522d8e91df584 to your computer and use it in GitHub Desktop.
Save mzipay/ea4f89e522d8e91df584 to your computer and use it in GitHub Desktop.
Python lazy non-data descriptor
# -*- coding: utf-8 -*-
# Copyright (c) 2015 Matthew Zipay <mattz@ninthtest.net>. All rights reserved.
# Licensed under the MIT License <http://opensource.org/licenses/MIT>.
class lazy:
r"""Decorate a no-arg getter to have lazy initialization behavior.
The first time the property is accessed on an instance, the computed
value is stored in the instance ``__dict__`` (using the same name as
the getter). Subsequent access returns the already-computed value as
a simple instance attribute.
This works because an attribute in an instance ``__dict__`` takes
precendence over a same-named, non-data descriptor in a class
``__dict__``.
>>> class Example:
... @lazy
... def expensive(self):
... print("initializing...")
... return "the computed value"
...
>>> x = Example()
>>> x.expensive
initializing...
'the computed value'
>>> x.expensive
'the computed value'
"""
def __init__(self, fget):
"""
:param fget: a no-arg method that computes and returns a value
"""
self._fget = fget
def __get__(self, obj, obj_type=None):
"""Return the computed value after setting it as an instance
attribute with the same name as the descriptor.
:param obj: an instance of the class that owns the getter
:keyword obj_type: the class of *obj*
:return: the value computed and returned by the getter
"""
if (obj is not None):
value = self._fget(obj)
setattr(obj, self._fget.__name__, value)
return value
# just return the descriptor itself if there's no instance
return self
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment