Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Python custom initialization on first class attribute access

The goal is to create a custom python class, that allows for lazyloading of attributes (a fairly common question here with a variety of different solutions) but that only runs the expensive initialization step once ON 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 @memoize decorator handles the caching step for me.

If I'm defining my LazyDateModule elsewhere:

LDM = LazyDateModule()

How do I run the _expensive_init() ONLY on the first time any of the memoized attribute methods were accessed? 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 that's not very clean. Ideally I'd like this behavior to either be at a class-level—but I've struggled in my attempts to overwrite __getattr__ or __getattribute__. Another decorator would also work.

I'm sorry if the above was confusing. Let me know if I can clarify it in any way!

EDIT 1: I think the above example is a little too simple. Let's say my _expensive_init() does a bunch of stuff, as opposed to just defining a single 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 would handle a variety of different behaviors.

like image 502
dizzyf Avatar asked Feb 05 '26 06:02

dizzyf


1 Answers

You have a caching decorator memoize, so why not use it to cache your _expensive_date_attr ? and BTW turn 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
like image 112
Guillaume Avatar answered Feb 07 '26 20:02

Guillaume



Donate For Us

If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!