Dabeaz

Dave Beazley's mondo computer blog. [ homepage ]

Tuesday, February 02, 2010

 

A function that works as a context manager and a decorator

As a followup to my last blog post on timings, I present the following function which works as both a decorator and a context manager.

# timethis.py
import time
from contextlib import contextmanager

def timethis(what):
    @contextmanager
    def benchmark():
        start = time.time()
        yield
        end = time.time()
        print("%s : %0.3f seconds" % (what, end-start))
    if hasattr(what,"__call__"):
        def timed(*args,**kwargs):
            with benchmark():
                return what(*args,**kwargs)
        return timed
    else:
        return benchmark()

Here is a short demonstration of how it works:

# Usage as a context manager
with timethis("iterate by lines (UTF-8)"):
     for line in open("biglog.txt",encoding='utf-8'):
          pass

# Usage as a decorator
@timethis
def iterate_by_lines_latin_1():
    for line in open("biglog.txt",encoding='latin-1'):
        pass

iterate_by_lines_latin_1()

If you run it, you'll get output like this:

bash % python3 timethis.py
iterate by lines (UTF-8) : 3.762 seconds
<function iterate_by_lines_latin_1 at 0x100537958> : 3.513 seconds

Naturally, this bit of code would be a good thing to bring into your next code review just to make sure people are actually paying attention.


Comments:
This is awful. For me at least. I'm working on a project for the next few months that requires Python 2.4, and every time I see a great solution like this... I can't use it. Oh well.
 
A suggestion to improve it slightly: also accept
@timethis("some comment")
def timedfn(..)
 
I'm half-tempted to integrate something like this into profilehooks so I could use those @profile or @timecall decorators as context managers.

OTOH I'd rather return an object that has __call__, __enter__ and __exit__ instead of doing checks inside and returning either one or the other.
 
Must say that I prefer the context manager over the decorator in this and similar cases - too often I find decorated code to be inflexible.

I'm sure there are lots of good things yet to come with context managers. Loved withhacks (explained on Reddit by Neat Hack to Get Anonymous Blocks in Python Using the With-Statement).

My own much more modest contributions are DRY up your routes and Fun with parameter-collecting proxies.
 
Post a Comment





<< Home

Archives

08/01/2009 - 09/01/2009   09/01/2009 - 10/01/2009   10/01/2009 - 11/01/2009   11/01/2009 - 12/01/2009   12/01/2009 - 01/01/2010   01/01/2010 - 02/01/2010   02/01/2010 - 03/01/2010   04/01/2010 - 05/01/2010  

This page is powered by Blogger. Isn't yours?

Subscribe to Posts [Atom]