A context manager for temporary memoization.

Carl Banks pavlovevidence at gmail.com
Thu Nov 29 17:43:42 EST 2007


On Nov 29, 3:20 pm, "Michael Speer" <knome... at gmail.com> wrote:
> I posted this to my blog athttp://michaelspeer.blogspot.com/2007/11/context-manager-for-temporar....
>
> I decided to forward it onto the list for comments.  I thought someone
> might find it interesting.
>
> ***
>
> This is very much a fragile hack at the moment. It's an interesting
> idea I think. I was disappointed when I initially found that the
> with_statement syntax does not restore the value of the `as var` upon
> completion.
>
> This made doing something along the lines of
>
>    with temporarily_memoized( func ) :
>      for datum in data :
>        func( datum )
>
> unattainable.
>
> Well, just a lot hackier actually.
>
> Thus temporarily_memoized( ... ) was born :
>
> #!/usr/bin/python
>
> # a context manager for temporarily turning any function into
> # a memoized version of itself.
>
> from __future__ import with_statement
> import contextlib , sys , types
>
> def memoize ( func ) :
>      """ In haskell mutability must be handled explicitly.
>      Only fair that Python do the same to transparent functionality
>      """
>      remembered = {}
>      def memoized ( *args ) :
>           """ memoized version of function """
>           if args in remembered :
>                return remembered[ args ]
>           else :
>                new = func( *args )
>                remembered[ args ] = new
>                return new
>      return memoized
>
> @contextlib.contextmanager
> def temporarily_memoized ( func ) :
>      """ memoize the given function for the duration of this block
>      save anything in the local variables that gets in the way of
>      using this so that it can be restored afterward , the memoized
>      version is found in the locals.  use on actual functions only.
>      no members. """
>
>      # this is being called, there has to be a frame above it
>      frame = sys._getframe().f_back.f_back
>
>      if func.func_name in frame.f_locals :
>           f = frame.f_locals[ func.func_name ]
>           frame.f_locals[ func.func_name ] = memoize( func )
>           try :
>                # this hack replaces whatever in the local scope
>                # has the name of the variable.  if you try to use
>                # the 'as whatever' portion of the syntax , you
>                # are doing it wrong
>                yield None
>           finally :
>                frame.f_locals[ f.func_name ] = f
>      else :
>           frame.f_locals[ func.func_name ] = memoize( func )
>           try :
>                yield None
>           finally :
>                del frame.f_locals[ func.func_name ]
>
> def fib(n):
>      """ biggus fibbus """
>      if n == 0 or n == 1:
>           return n
>      else:
>           return fib(n-1) + fib(n-2)
>
> if __name__ == '__main__' :
>      print fib.__doc__
>      with temporarily_memoized( fib ) :
>           print fib.__doc__
>           for i in xrange( 36 ) :
>                print "n=%d => %d" % (i, fib(i))
>           print fib.__doc__
>      print fib.__doc__
>
> outputs :
>
>  biggus fibbus
>  memoized version of function
> n=0 => 0
> .....
> n=35 => 9227465
>  memoized version of function
>  biggus fibbus


Did you try to use temporarily_itemized inside a function?  It might
not work as you think.

BTW, the name temporarily_memoized is superfluous.  By definition,
whatever a context manager does is temporary, so using "temporarily"
in the name just adds a lot of letters and tells us nothing we didn't
already know.

Just call it memoized.


Carl Banks



More information about the Python-list mailing list