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