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.

+3


source to share


1 answer


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

      

+2


source







All Articles