Python 首次 class 属性访问时的自定义初始化

Python custom initialization on first class attribute access

目标是创建一个自定义 python class,它允许延迟加载属性(这里是一个相当常见的问题,有多种不同的解决方案),但只有 运行一旦延迟访问,这是昂贵的初始化步骤。这是一个例子:

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

在此示例中,我的 @memoize 装饰器为我处理缓存步骤。

如果我在别处定义我的 LazyDateModule:

LDM = LazyDateModule()

如何仅在第一次访问任何记忆化属性方法时 运行 _expensive_init()?我试过这样的事情:

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

但这不是很干净。理想情况下,我希望此行为处于 class 级别 — 但我在尝试覆盖 __getattr____getattribute__ 时遇到了困难。另一个装饰器也可以。

如果以上内容令人困惑,我深表歉意。如果我能以任何方式澄清它,请告诉我!

编辑 1: 我觉得上面的例子有点太简单了。假设我的 _expensive_init() 做了很多事情,而不是仅仅定义一个属性。

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...

理想情况下,此方法将处理各种不同的行为。

您有一个缓存装饰器 memoize,那么为什么不用它来缓存您的 _expensive_date_attr 呢?顺便说一句,把它变成 属性:

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