0

Following PEP-562:

https://www.python.org/dev/peps/pep-0562/

it is now possible to define attribute for a module. Unfortunately some of the built-in component hasn't adapt to this new feature yet. In the following code:

@property
def lazyFn():
    return 3

v = lazyFn

v

Ideally v should have the attribute value 3. Unfortunately, the currently implementation of @property only yield the following result:

<property at 0x7f3e703eae30>

How do I fix it to behave as expected? Or alternatively, where can I find a replacement that has the proper implementation?

tribbloid
  • 4,823
  • 10
  • 46
  • 81
  • 2
    Where do you get the idea this is supposed to work for `@property`? The PEP seems pretty focused on `__getattr__` and `__dir__` exclusively. It's not claiming to add full descriptor support (which is what `property` relies on). – ShadowRanger Oct 19 '19 at 18:01
  • You are right, the PEP and the decorator are indeed independent but it created a divergence. I'm simply looking for a library that behave consistently on both __getAttr__ – tribbloid Oct 19 '19 at 18:09
  • Even if modules were supported, there's no reason to believe it should work without an attribute lookup, and there's no "self" equivalent *in* a module. (One *could* imagine that `import foo; assert foo.lazyFn == 3` would succeed, though.) – chepner Oct 19 '19 at 18:21
  • there is a "self" equivalence: python module can be manipulated with reflection. Also, what is the point of `import foo` if lazyFn is defined in the same file? – tribbloid Oct 19 '19 at 18:35
  • This [answer](https://stackoverflow.com/a/52018676/355230) to another question would be a way to do it. – martineau Oct 19 '19 at 18:59
  • @martineau that looks promising but it is too verbose even in C++ standard, I'm looking for a short decorator – tribbloid Oct 19 '19 at 19:04
  • 1
    tribbloid: Dunno what C++ standards have to do with anything. Regardless, the linked answer isn't taking advantage of PEP 562, so there may be a way to implement the idea in a more terse fashion. – martineau Oct 19 '19 at 19:10

1 Answers1

0

I can't believe how easy the answer could be (and how this is not a built-in or bolt-on), there is no need for reflection, module, setattr, or PEP-562. All I need is to define a decorator:


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

to test it:

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])

please correct me if this implementation is defective

tribbloid
  • 4,823
  • 10
  • 46
  • 81