如果装饰器之前是 None,如何让装饰器 return a(缓存)属性?

How to have a decorator return a (cached) property iff it was previously None?

在一个 class 中,参数是可选的并且是其他计算的,我通常有类似

class Demo(object):
    def __init__(self, value=None):
        self._value = value

    @property
    def value(self):
        if self._value is None:
            # some calculations to get value
            self._value = value
        return self._value

恕我直言,这非常嘈杂,类似于记忆序言。我更喜欢

    @property_if_None
    def value(self):
        # some calculations to get value
        return value

如果 value 不是可选的,只是没有设置,我会使用 @functools.lru_cached,但这行不通。


当然我可以简单地将__init__更改为

    def __init__(self, value=None):
        if value is None:
            # some calculations to get value
        self.value = value

但我们假设 # some calculations to get value 很长并且应该在它自己的函数中 - 我更愿意调用 value() 从而有效地使 value 成为 @property,但当且仅当它未设置为值≠None...

可能不是最完美的解决方案,但这是我的做法:

import functools

# 
def property_if_None_generator(f, cached=False):
    @functools.wraps(f)
    def wrapped(self):
        value = self.__getattribute__('_' + f.__name__)
        if value is not None:
            return value
        else:
            return f(self)
    if cached:
        wrapped = functools.lru_cache()(wrapped)
    return property(wrapped)

property_if_None = lambda f: property_if_None_generator(f, cached=False)
cached_property_if_None = lambda f: property_if_None_generator(f, cached=True)

那么问题的例子就会变成

class Demo(object):
    def __init__(self, value=None):
        self._value = value

    @chached_property_if_None
    def value(self):
        # some calculations to get value
        return value