Memoizing decorator

Daishi Harada daishi at gmail.com
Wed Dec 7 16:05:10 EST 2005


Hi Bengt,

Thanks for your reply.

>First, I would suggest thinking about the exact semantics of method
>memoization. E.g. suppose you write
>
>   class Oops(object):
>        def __init__(self, factor): self.factor = factor
>        @memoize_fn
>        def mul(self, x):
>            return self.factor * x
>
>You would need a separate cache for every instance to use x as a cache lookup key,
>or look up with say (x, id(self)), either way assuming that self.factor can't change.

You're right; I wasn't thinking clearly enough.
I was in fact hoping to get the same memoize
to work with classmethods also.

I wonder if you wouldn't mind taking a look at
the following - it seems to "work" as given,
but I'm likely missing something again.
In particular, if one flips the order of the
'classmethod' and 'memoize_fn' decorators,
I get a 'classmethod not callable' error,
and I'm losing track of what's really going on.

(I've also "pushed" all of your logic into a
single descriptor class - am I forgetting
something by doing so?)

Thanks again and in advance for your
response,

Daishi

---

def test_memoize(memoize_fn):
    def printsep():
        print '-'*30

    @memoize_fn
    def fn(x):
        print '(eval %s)' % str(x),
        return x
    print 'fn', fn(1); printsep()
    print 'fn', fn(1); printsep()

    class A(object):
        n_insts = 0
        def __init__(self, n):
            self.n = n
            A.n_insts += 1

        @memoize_fn
        def fn(self, x):
            print '(eval %s %s)' % (str(self), str(x))
            return x+self.n

        # The following will memoize the wrong answer
        # unless all instances are created first.
        @classmethod
        @memoize_fn
        def clsfn(cls, x):
            print '(clsmeth %s %s)' % (str(cls), str(x))
            return x+cls.n_insts

    def test_class(a):
        print 'meth', a.fn(1); printsep()
        print 'meth', a.fn(1); printsep()
        print 'clsmeth', A.clsfn(1); printsep()
        print 'clsmeth', A.clsfn(1); printsep()
    a1 = A(1)
    a2 = A(2)
    test_class(a1)
    test_class(a2)

class DescriptorMemoize(object):
    def __init__(self, fn):
        self.fn = fn
        self._cache = {}
        self._inst = None
    def __get__(self, inst, owner):
        print '__get__', inst, owner
        self._inst = inst
        return self
    def __call__(self, *args):
        if self._inst is not None:
            args = (self._inst,)+args
        try:
            return self._cache[args]
        except KeyError:
            value = self.fn(*args)
            self._cache[args] = value
            return value
        except TypeError:
            return self.fn(*args)




More information about the Python-list mailing list