3

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.

4
  • 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? Commented May 29, 2016 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. Commented May 29, 2016 at 19:54
  • 1
    Perhaps this one describing a lazily calculated class attribute would be of use. You'd use it here with @class_reify def expensive_property(): return SomeExpensiveToSetUpClass() Commented May 29, 2016 at 20:16
  • Sorry, that'd be def expensive_property(cls) Commented May 29, 2016 at 20:20

1 Answer 1

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.

Sign up to request clarification or add additional context in comments.

5 Comments

What do you mean it is close? This is exactly what that answer details, the x = 4 is that users specific case.
I apologize: in that answer I don't see why Foo.x isn't enough. Maybe I'm missing something?
I read that example as suggesting that I had to define _expensive_property outside of the expensive_property method as well.
That is the point of except AttributeError in the property code, if it is not already defined it is defined the first time it is tried to be used.
Exactly...that's how they differ.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.