DEV Community

Mike Lezhnin
Mike Lezhnin

Posted on

Python `property`

Welp, I discovered descriptors in python. I was trying to implement something similar to @property, which I thought of as some weird decorator, which turns a function into some sort of a function that is called without the () brackets.
Of course an automatically called function is something impossible, but one could inject something into cls.__getattr__ perhaps? Turns out this is also impossible (afaik) - to get to the class one has to either hook into where the function is defined; or have something called to reach self and then hook into __getattr__, but we don't have anything being called...

After understanding that I got no clue how to create property on my own, I googled for that, and now I got this:

class prop:
    def __init__(self, fget):
        self.fget = fget
    def __get__(self, obj, objtype=None):
        return self.fget(obj)

class A:
    @prop
    def data(self):
        return 32
Enter fullscreen mode Exit fullscreen mode

If I produce an object with a method __get__(self, obj, objtype=None) inside another class, then this object is magically called without any brackets. Yeap, descriptors.

Btw, here is a link for more info: https://docs.python.org/3/howto/descriptor.html#properties

As for what I wanted to do, I now got this:

def lazy(load='scan'):
    class desc:
        def __init__(self, prop):
            self.attr = f'_{prop.__name__}'
        def __get__(self, obj, objtype = None):
            if not hasattr(obj, self.attr) or getattr(obj, self.attr) == None:
                getattr(obj, load)()
            return getattr(obj, self.attr)
    return desc

class A:
    def __init__(self):
        pass
    @lazy()
    def data(): pass
    @lazy()
    def other(): pass
    def scan(self):
        self._data = 42
        self._other = 34
Enter fullscreen mode Exit fullscreen mode

Now I can have multiple properties lazily loaded when they are needed. Yeaay.

Top comments (0)