2

I have two classes, one of which is expensive to set up, but reusable, and the other of which will have many instances in my application, but can reuse an instance of the expensive class. This is easier to explain by example:

class SomeExpensiveToSetUpClass(object):
    def __init__(self):
        print("Expensive to set up class initialized")
        self.whatever = "Hello"

    def do_the_thing(self):
        print(self.whatever)

class OftenUsedClass(object):

    @staticmethod
    @property
    def expensive_property():
        try:
            return OftenUsedClass._expensive_property
        except AttributeError:
            OftenUsedClass._expensive_property = SomeExpensiveToSetUpClass()
            return OftenUsedClass._expensive_property

    # I know I could hide the static property in an instance property:
    @property
    def expensive_property2(self):
        try:
            return OftenUsedClass._expensive_property
        except AttributeError:
            OftenUsedClass._expensive_property = SomeExpensiveToSetUpClass()
            return OftenUsedClass._expensive_property
    #
    # And then:
    #
    # ouc = OftenUsedClass()
    # ouc.expensive_property2.do_the_thing()
    # ouc.expensive_property2.do_the_thing()
    # ouc.expensive_property2.do_the_thing()
    #
    # but that feels misleading

if __name__ == '__main__':
    OftenUsedClass.expensive_property.do_the_thing()
    OftenUsedClass.expensive_property.do_the_thing()
    OftenUsedClass.expensive_property.do_the_thing()

As you can, see, I was hoping I would use @staticmethod and @property to essentially memoize the property on the first use of the property, but no dice--I'm getting an instance of property back instead:

Traceback (most recent call last):
  File "memo.py", line 39, in <module>
    OftenUsedClass.expensive_property.do_the_thing()
AttributeError: 'property' object has no attribute 'do_the_thing'

I've found several patterns for memoization decorators, but none for static properties. Am I missing something? Or is there an alternate pattern I should be using?

Edit:

I oversimplified my question: I should have included that the name of the SomeExpensiveToSetUpClass class implementation is supplied in a config file, and so I don't know its name at until the first time OftenUsedClass is instantiated.

JStroop
  • 475
  • 1
  • 8
  • 21
  • 1
    I'm not sure if I follow you correctly since your example is rather convoluted, but cannot you convert `SomeExpensiveToSetUpClass()` to singleton (as there is only one instance of that class) and after that dispatch all required calls in "OftenUsedClass" to that singleton? – Łukasz Rogalski May 29 '16 at 19:32
  • @Rogalski - thanks! I added a clarification as to why singleton isn't possible. Sorry I wasn't clear enough the first time. – JStroop May 29 '16 at 19:54
  • 1
    Perhaps this one [describing a lazily calculated class attribute](http://stackoverflow.com/questions/18289871/lazy-class-property-decorator) would be of use. You'd use it here with `@class_reify def expensive_property(): return SomeExpensiveToSetUpClass()` – Antti Haapala -- Слава Україні May 29 '16 at 20:16
  • Sorry, that'd be `def expensive_property(cls)` – Antti Haapala -- Слава Україні May 29 '16 at 20:20

1 Answers1

1

The suggested possible duplicate is close. The classmethod decorator works as described in that answer:

class classproperty(object):
    def __init__(self, getter):
        self.getter = getter
    def __get__(self, instance, owner):
        return self.getter(owner)

but there's no need to define anything outside of the class, so:

class OftenUsedClass(object):

    @classproperty
    def expensive_property3(cls):
        try:
            return cls._expensive_property
        except AttributeError:
            cls._expensive_property = SomeExpensiveToSetUpClass()
            return cls._expensive_property

works fine.

Community
  • 1
  • 1
JStroop
  • 475
  • 1
  • 8
  • 21