Я не могу поверить, насколько простым может быть ответ (и как он не является встроенным или незакрепленным), нет необходимости в рефлексии, модуле, setattr или PEP-562. Все, что мне нужно, это определить декоратор:
def lazy(fn):
if fn.__name__ == fn.__qualname__:
# not a property
result = fn()
return result
else:
return LazyProperty(fn)
# the following are from PyPI lazy library
class LazyProperty(object):
"""lazy descriptor
Used as a decorator to create lazy attributes. Lazy attributes
are evaluated on first use.
"""
def __init__(self, func):
self.__func = func
functools.wraps(self.__func)(self)
def __get__(self, inst, inst_cls):
if inst is None:
return self
if not hasattr(inst, '__dict__'):
raise AttributeError("'%s' object has no attribute '__dict__'" % (inst_cls.__name__,))
name = self.__name__
if name.startswith('__') and not name.endswith('__'):
name = '_%s%s' % (inst_cls.__name__, name)
value = self.__func(inst)
inst.__dict__[name] = value
return value
, чтобы проверить его:
nn = 0
@lazy
def fn1():
global nn
nn = nn + 1
return nn
@dataclass
class HasFn2(object):
nn = 0
@lazy
def fn2(self):
self.nn = self.nn + 1
return self.nn
def test_lazy():
vs1 = [fn1 for i in range(0, 5)]
assert vs1 == [1, 1, 1, 1, 1]
c = HasFn2()
vs2 = [c.fn2 for i in range(0, 5)]
assert (vs2 == [1, 1, 1, 1, 1])
Пожалуйста, исправьте меня, если эта реализация неисправна