Fork me on GitHub

Posts tagged decorators

Lazy Properties & a Nifty Decorator

I sometimes use properties as a form of cache. You see this in Brubeck with current_user and current_userprofile. The idea is that if you don’t need access to the current user, the message handler won’t attempt to load it. If you do need it, the first time you try to access it will trigger the loading mechanism and off you go.

HT Tornado - I originally saw this technique in Tornado’s source code.

Basically, this is a lazy loading technique for attributes that people might use. However, if you do this frequently, as I’m doing right now to cache the compilation of WSDL files, you find yourself staring at a lot of boiler-plate code. Boooooooo.

I was mentioning this to my friend Alan and he asked why it couldn’t be done with a decorator. Well… He’s totally right. It can be.

A quick google turned up this stackoverflow link, which had the answer. I made a slight adjustment, but the general logic is still the same.

def lazyproperty(method):
    attr_name = '_' + method.__name__
    @property
    def _lazyprop(self):
        if not hasattr(self, attr_name):
            setattr(self, attr_name, method(self))
        return getattr(self, attr_name)
    return _lazyprop    

The basic idea is that this decorator will create an attribute with the same name as the function you’re decorating. This attribute will cache the value returned by your function.

Example

Below we define a class called Foo which has a function called foo. We want foo to be our property.

Notice that foo() has a 5 second sleep in it. This will simulate something like loading a value from a database.

>>> class Foo(object):
...     @lazyprop
...     def foo(self):
...         print 'Calculating foo, which might take a long time'
...         time.sleep(5)
...         return 'foo'
... 

Don’t forget to import time.

>>> import time

OK. We’ll instantiate Foo and see what happens when we access our property foo. Before we do that, though, we’ll check the value of __dict__ to show that there are no cached values. After accessing the property we will see that self._foo exists, serving as our cache.

>>> f = Foo()
>>> f.__dict__
{}
>>> f.foo
Calculating foo, which might take a long time
'foo'
>>> f.foo
'foo'
>>> f.__dict__
{'_foo': 'foo'}

I really like how clean this approach is. Some folks find the use of Python properties less explicit than they’d prefer. If you have an opinion, please share it in the comments.

Updates!

Thanks @whitmo for informing me that the Pyramid project has a similar decorator: click here.

See more
This post has 9 notes
Posted at 3:27 PM 15 February 2012

Logging Function Calls with Decorators

I am one of those coders that tends to write verbose log output. Especially during early development stages of a system when you’re not sure your code is correct yet.

Python’s logging module leaves some things to be desired, but it’s an excellent default offering. Especially when used in a higher-order manner, like with a Python Decorator.

Logging

Starting a log can be two lines of code.

import logging
logging.basicConfig(level=logging.DEBUG, filename='whatevz.log')

From there, you call a function like logging.debug(...) or logging.info(...), depending on how important you think the output is. Anything below the level you specified in the config will be ignored.

Checking Runtime With A Decorator

Decorators are handy tools. I can easily wrap some logging around a function without changing with how a coder uses that function.

Here is a simple decorator to log how long it takes a function to run. This function could be more robust, but I kept it simple for the example’s simplicity.

def log_runtime(method):
    def wrapper(*a, **kw):

        start = time.time()
        retval = method(*a, **kw)
        finish = time.time()

        logging.debug('%s took %s seconds to run' % (method.__name__,
                                                     (finish - start)))
        return retval
    return wrapper

So before calling method we check the time. Then we call method and check the time again and print the difference to the log.

Using it then looks like this.

@log_runtime
def foo():
    print 'foo'

You’ll have a line in your log that looks like this, after foo has been called.

DEBUG:root:foo took 2.38418579102e-05 seconds to run

Try running it and look for a whatevz.log in the same directory as the code.

See more
This post has 34 notes
Tagged with code, decorators, logging, python,
Posted at 7:03 PM 23 June 2011