I am trying to create a decorator that will work for methods to apply a “cooldown” to them, that is, they cannot be called several times for a certain duration. I already created one for the functions:
>>> @cooldown(5) ... def f(): ... print('f() was called') ... >>> f() f() was called >>> f()
but I need this to support class methods instead of regular functions:
>>> class Test: ... @cooldown(6) ... def f(self, arg): ... print(self, arg) ... >>> t = Test() >>> tf(1) <Test object at ...> 1 >>> tf(2) >>> tf(5)
Here is what I created to make it work for normal functions:
import time class _CooldownFunc: def __init__(self, func, duration): self._func = func self.duration = duration self._start_time = 0 @property def remaining(self): return self.duration - (time.time() - self._start_time) @remaining.setter def remaining(self, value): self._start_time = time.time() - (self.duration - value) def __call__(self, *args, **kwargs): if self.remaining <= 0: self.remaining = self.duration return self._func(*args, **kwargs) def __getattr__(self, attr): return self._func.__getattribute__(attr) def cooldown(duration): def decorator(func): return _CooldownFunc(func, duration) return decorator
But this does not work with methods, because it passes the _CooldownFunction object as self and completely ignores the original self . How do I get it to work with methods by correctly passing the original self instead of the _CooldownFunction object?
In addition, users should be able to change the remaining time on the fly, which makes it even more difficult (cannot just use __get__ to return functools.partial(self.__call__, obj) or something else):
>>> class Test: ... @cooldown(10) ... def f(self, arg): ... print(self, arg) ... >>> t = Test() >>> tf(5) <Test object at ...> 5 >>> tfremaining = 0 >>> tf(3)
Edit: It should work only for methods, not for both methods and functions.
Edit 2: There is a huge flaw in this design. Although it works fine for normal functions, I want it to decorate each instance separately. Currently, if I needed to have two instances of t1 and t2 and had to call t1.f() , I could no longer call t2.f() because the recovery time was teid for the f() method instead of instances. I could probably use some kind of vocabulary for this, but after this realization I lost even more ...