Using arguments in a decorator

Ian Kelly ian.g.kelly at gmail.com
Fri Apr 20 20:07:02 EDT 2012


On Fri, Apr 20, 2012 at 9:57 AM, Rotwang <sg552 at hotmail.co.uk> wrote:
> As far as I know, the decorated function will always return the same value
> as the original function. The problem is that the dictionary key stored
> depends on how the function was called, even if two calls should be
> equivalent; hence the original function gets called more often than
> necessary. For example, there's this:
>
>>>> @memo
> def f(x, y = None, *a, **k):
>        return x, y, a, k
>
>>>> f(1, 2)
> (1, 2, (), {})
>>>> f.d
> {((1, 2), ()): (1, 2, (), {})}
>>>> f(y = 2, x = 1)
> (1, 2, (), {})
>>>> f.d
> {((1, 2), ()): (1, 2, (), {}), ((), (('y', 2), ('x', 1))): (1, 2, (), {})}


Using the FunctionMaker from the third-party decorator module [1]:

from copy import copy
import inspect
from decorator import FunctionMaker

memo_body = """
key = (%s)
if key not in cache:
    cache[key] = func(%%(signature)s)
return copy(cache[key])
"""

def memo(func):
    (args, varargs, varkw, defaults) = inspect.getargspec(func)
    if varargs:
        args.append(varargs)
    if varkw:
        args.append("tuple(sorted(%s.items()))" % varkw)
    cache = {}
    decorated = FunctionMaker.create(func, memo_body % ', '.join(args),
                                     dict(func=func, cache=cache, copy=copy))
    decorated.d = cache
    return decorated

>>> @memo
... def f(x, y=None, *a, **k):
...     return x, y, a, k
...
>>> f(1, 2)
(1, 2, (), {})
>>> f.d
{(1, 2, (), ()): (1, 2, (), {})}
>>> f(y=2, x=1)
(1, 2, (), {})
>>> f.d
{(1, 2, (), ()): (1, 2, (), {})}


Cheers,
Ian

[1] http://pypi.python.org/pypi/decorator



More information about the Python-list mailing list