Python custom initialization when accessing first class attributes
The goal is to create a custom python class that allows lazyloading of attributes (a fairly common question here with a lot of different solutions), but this only triggers an expensive initialization step when enabled in lazy access. Here's an example:
class LazyDateModule()
def __init__(self, args):
self.args = args
self._expensive_date_attr = None
def _expensive_init():
time.sleep(10)
self._expensive_date_attr = date.today()
@memoize
def date_str():
return str(self._expensive_date_attr)
@memoize
def month_num():
return self._expensive_date_attr.month
In this example, my constructor @memoize
handles the caching step for me.
If I define my LazyDateModule elsewhere:
LDM = LazyDateModule()
How to run _expensive_init()
ONLY the first time when accessing any of the memoized attribute methods? I've tried something like this:
class LazyDateModule()
def __init__(self, args):
self.args = args
self._assembled = False
self._expensive_date_attr = None
def _expensive_init():
time.sleep(10)
self._expensive_date_attr = date.today()
self._assembled = True
@memoize
def date_str():
if self._assembled is False:
self._expensive_init()
return str(self._expensive_date_attr)
@memoize
def month_num():
if self._assembled is False:
self._expensive_init()
return self._expensive_date_attr.month
But it's not very clean. Ideally, I would like this behavior to be either at the class level, but I struggled to overwrite __getattr__
or __getattribute__
. Another decorator will work as well.
I apologize if there was confusion above. Let me know if I can clarify this!
EDIT 1: I think the above example is too simple. Let's say my _expensive_init () does a bunch of things and doesn't just define one attribute.
def _expensive_init(self):
time.sleep(5)
self._expensive_date_attr = date.today()
time.sleep(1)
# send a message to someone saying that this occurred
time.sleep(1)
# run a db call to update when this action occurred
etc...
Ideally, this method will handle different types of behavior.
source to share
You have a cache decoder memoize
, so why not use one to cache yours _expensive_date_attr
? and BTW turns it into a property:
class LazyDateModule():
def __init__(self, args):
self.args = args
self._assembled = False
@memoize
@property
def _expensive_date_attr(self):
return date.today
@memoize
def date_str():
return str(self._expensive_date_attr)
@memoize
def month_num():
return self._expensive_date_attr.month
source to share