wrapt icon indicating copy to clipboard operation
wrapt copied to clipboard

Descriptors don't seem to work, if they're mixed in

Open AlexanderFarkas opened this issue 1 year ago • 2 comments


class memoized_property(object):
    """A read-only @property that is only evaluated once."""

    def __init__(self, fget, doc=None):
        self.fget = fget
        self.__doc__ = doc or fget.__doc__
        self.__name__ = fget.__name__

    def __get__(self, obj, cls):
        if obj is None:
            return self
        obj.__dict__[self.__name__] = result = self.fget(obj)
        return result
    
class B:
    @memoized_property
    def c(self):
        return WeakKeyDictionary()

class Proxy(wrapt.ObjectProxy, B):
    pass
    
if __name__ == '__main__':
    b = ProxySon(B())
    print(b.c)
    print(b.c)

Output:

<WeakKeyDictionary at 0x1109a50d0>
<WeakKeyDictionary at 0x1109a51d0>
!!! not the same

AlexanderFarkas avatar Jan 19 '23 02:01 AlexanderFarkas

Sorry for slow reply, was on holiday at the time and just catching up.

As far as I can tell your implementation of __get__() is wrong as you don't check to see whether you already cached the value and return that. Instead you call self.fget() every time. I would expect something like:

import wrapt

from weakref import WeakKeyDictionary

class memoized_property(object):
    """A read-only @property that is only evaluated once."""

    def __init__(self, fget, doc=None):
        self.fget = fget
        self.__doc__ = doc or fget.__doc__
        self.__name__ = fget.__name__

    def __get__(self, obj, cls):
        if obj is None:
            return self
        if not self.__name__ in obj.__dict__:
            result = obj.__dict__[self.__name__] = self.fget(obj)
        else:
            result = obj.__dict__[self.__name__]
        return result

class B:
    @memoized_property
    def c(self):
        return WeakKeyDictionary()

class Proxy(wrapt.ObjectProxy, B):
    pass

if __name__ == '__main__':
    b = Proxy(B())
    print(b.c)
    print(b.c)

GrahamDumpleton avatar Jan 30 '23 23:01 GrahamDumpleton

It's the implementation taken directly from the SQLAlchemy library. Descriptors are not called if there is an item in __dict___ with the corresponding name - so implementation is perfectly valid.

AlexanderFarkas avatar Apr 02 '23 15:04 AlexanderFarkas