Python class decorator arguments
@Cache(max_hits=100, timeout=50)
calls __init__(max_hits=100, timeout=50)
, so you aren't satisfying the function
argument.
You could implement your decorator via a wrapper method that detected whether a function was present. If it finds a function, it can return the Cache object. Otherwise, it can return a wrapper function that will be used as the decorator.
class _Cache(object):
def __init__(self, function, max_hits=10, timeout=5):
self.function = function
self.max_hits = max_hits
self.timeout = timeout
self.cache = {}
def __call__(self, *args):
# Here the code returning the correct thing.
# wrap _Cache to allow for deferred calling
def Cache(function=None, max_hits=10, timeout=5):
if function:
return _Cache(function)
else:
def wrapper(function):
return _Cache(function, max_hits, timeout)
return wrapper
@Cache
def double(x):
return x * 2
@Cache(max_hits=100, timeout=50)
def double(x):
return x * 2
@Cache
def double(...):
...
is equivalent to
def double(...):
...
double=Cache(double)
While
@Cache(max_hits=100, timeout=50)
def double(...):
...
is equivalent to
def double(...):
...
double = Cache(max_hits=100, timeout=50)(double)
Cache(max_hits=100, timeout=50)(double)
has very different semantics than Cache(double)
.
It's unwise to try to make Cache
handle both use cases.
You could instead use a decorator factory that can take optional max_hits
and timeout
arguments, and returns a decorator:
class Cache(object):
def __init__(self, function, max_hits=10, timeout=5):
self.function = function
self.max_hits = max_hits
self.timeout = timeout
self.cache = {}
def __call__(self, *args):
# Here the code returning the correct thing.
def cache_hits(max_hits=10, timeout=5):
def _cache(function):
return Cache(function,max_hits,timeout)
return _cache
@cache_hits()
def double(x):
return x * 2
@cache_hits(max_hits=100, timeout=50)
def double(x):
return x * 2
PS. If the class Cache
has no other methods besides __init__
and __call__
, you can probably move all the code inside the _cache
function and eliminate Cache
altogether.
I'd rather to include the wrapper inside the class's __call__
method:
UPDATE: This method has been tested in python 3.6, so I'm not sure about the higher or earlier versions.
class Cache:
def __init__(self, max_hits=10, timeout=5):
# Remove function from here and add it to the __call__
self.max_hits = max_hits
self.timeout = timeout
self.cache = {}
def __call__(self, function):
def wrapper(*args):
value = function(*args)
# saving to cache codes
return value
return wrapper
@Cache()
def double(x):
return x * 2
@Cache(max_hits=100, timeout=50)
def double(x):
return x * 2