6

Sometimes I like to write getter attributes for an object such that the first time they are called, the heavy lifting is done once, and that value is saved and returned on future calls. In objective-c I would use an ivar or a static variable to hold this value. Something like:

- (id)foo
{
    if ( _foo == nil )
    {
        _foo = // hard work to figure out foo
    }
    return _foo
}

Does this same pattern hold up well in Python, or is there a more accepted way of doing this? I have basically the same thing so far. What I don't like about my solution is that my object gets cluttered up with values and getters for those values:

def foo(self):
    if not hasattr(self, "mFoo":
        self.mFoo = # heavy lifting to compute foo
    return self.mFoo
D.C.
  • 15,340
  • 19
  • 71
  • 102

4 Answers4

7

Use a lazy property instead. Getters are so 1990's.

Community
  • 1
  • 1
Ignacio Vazquez-Abrams
  • 776,304
  • 153
  • 1,341
  • 1,358
3

Instead of doing an explicit "hasattr" test every time, let the Python runtime do that for you. Define __getattr__ in your class, which is only called when an undefined attribute is referenced.

class Sth(object):
    @property
    def a(self):
        print "property a"
        return self._a

    def _a_compute(self):
        # put expensive computation code here
        print "_a_compute"
        return 1000

    def __getattr__(self, attr):
        print "__getattr__"
        if attr == '_a':
            self._a = self._a_compute()
            return self._a


ss = Sth()
print "first time"
print ss.a
print
print "second time"
print ss.a

Prints the following:

first time
property a
__getattr__
_a_compute
1000

second time
property a
1000

You could leave out the property and have __getattr__ test for 'a' directly, but then you wouldn't have the visibility to 'a' as an attribute in dir for things like introspection or IDE autocomplete.

PaulMcG
  • 62,419
  • 16
  • 94
  • 130
  • thanks, i was confused at first between __getattr__ and __getattribute__. This makes sense though. – D.C. Mar 13 '12 at 04:12
1

You can use exactly the same pattern in Python. You seem to be worried about whether having to do my_object.get_foo() all the time is Pythonic. Thankfully, Python gives you a nice tool to work with here in form of properties:

class my_class(object):

     @property
     def foo(self):
       # calculate if needed
       return self._foo

This lets you have something that is used as an attribute, even if it is implemented as a function. ie, users will do my_object.foo, and not care that its running a function behind the scenes.

The other thing to note is that Python convention says that private attributes are spelled _foo rather than mFoo.

lvc
  • 34,233
  • 10
  • 73
  • 98
  • @darren indeed it doesn't, that's what I get for posting on the wrong side of midnight. :) I've fixed it now. – lvc Mar 13 '12 at 06:35
1

I'd do something like this:

@property
def foo(self):
    return self._foo_value if hasattr(self, '_foo_value') else calculate_foo()

def calculate_foo(self):
    self._foo_value = # heavy foo calculation
    return self._foo_value

Now you can access "foo" wether it was already calculated or not, using:

object.foo
Douglas Camata
  • 598
  • 4
  • 10