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