How would I write a decorator like this. I want to be able to specify the value for max_hits when I call the decorator (or optionally leave it out).
E.g., the desired use would be
@memoize(max_hits=7)
def a(val):
print val
or
@memoize
def a(val):
print val
(Using the first example gives an error about incorrect arguments.)
Decorator:
class memoize:
"""A decorator to cache previosly seen function inputs.
usage:
@memoize
def some_func(..
"""
def __init__(self, function, max_hits=None):
self.max_hits = max_hits
self.function = function
self.memoized = {}
def __call__(self, *args, **kwargs):
key = (args,tuple(kwargs.items()))
try:
return self.memoized[key]
except KeyError:
self.memoized[key] = self.function(*args,**kwargs)
return self.memoized[key]
You have to make memoize
a function that takes an optional argument max_hits
and returns a decorator (i.e. another callable object that will take the function as the first argument); in this case, you can use the following two syntaxes:
@memoize()
def func(x):
[...]
@memoize(max_hits=7)
def func(x):
[...]
So, probably something along the lines of:
def memoize(max_hits=None):
"""Returns a decorator to cache previosly seen function inputs.
usage:
@memoize()
def some_func(..
"""
class decorator:
def __init__(self, function):
self.max_hits = max_hits
self.function = function
self.memoized = {}
def __call__(self, *args, **kwargs):
key = (args,tuple(kwargs.items()))
try:
return self.memoized[key]
except KeyError:
self.memoized[key] = self.function(*args,**kwargs)
return self.memoized[key]
return decorator
Note that @memoize()
will work but your originally desired @memoize
syntax won't; in the latter case, the function you are decorating with @memoize
would be passed to memoize
as the first argument (max_hits
). If you want to treat this case, you can extend memoize
as follows:
def memoize(max_hits=None):
if callable(max_hits):
# For sake of readability...
func = max_hits
decorator = memoize(max_hits=None)
return decorator(func)
[...original implementation follows here...]
If you're using 3.2+, don't write it yourself, use from functools import lru_cache
instead
If you want to support the no-parenthesis version, it is also better to use a sentinel value for the function argument rather than introspecting the "wrong" argument. That is:
class Memoized(object):
# As per the memoize definition in the question
# Leave out the "*, " in 2.x, since keyword-only arguments are only in 3.x
from functools import partial
_sentinel = object()
def memoize(_f=_sentinel, *, max_hits=None):
if _f is _sentinel:
return partial(Memoized, max_hits=max_hits)
return Memoized(_f, max_hits=max_hits)
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With