I'm trying to pass optional arguments to my class decorator in python. Below the code I currently have:
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. @Cache def double(x): return x * 2 @Cache(max_hits=100, timeout=50) def double(x): return x * 2
The second decorator with arguments to overwrite the default one (max_hits=10, timeout=5
in my __init__
function), is not working and I got the exception TypeError: __init__() takes at least 2 arguments (3 given)
. I tried many solutions and read articles about it, but here I still can't make it work.
Any idea to resolve this? Thanks!
@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.
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