Belirli bir süre içinde birden çok kez çağrılmayacakları anlamına gelen bir "cooldown" uygulayabilecek yöntemler için çalışacak bir dekoratör oluşturmaya çalışıyorum. Zaten işlevleri için birini oluşturdu:İşlev yerine işlevler için bir cooldown dekoratörün değiştirilmesi
>>> @cooldown(5)
... def f():
... print('f() was called')
...
>>> f()
f() was called
>>> f() # Nothing happens when called immediately
>>> f() # This is 5 seconds after first call
f() was called
ama bu normal fonksiyonlarını yerine sınıfların yöntemleri desteklemek gerekir:
: İşte>>> class Test:
... @cooldown(6)
... def f(self, arg):
... print(self, arg)
...
>>> t = Test()
>>> t.f(1)
<Test object at ...> 1
>>> t.f(2)
>>> t.f(5) # Later
<Test object at ...> 5
Bu normal fonksiyonları için çalışmasını sağlamak için oluşturulan ne
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
Ancak, _CooldownFunction
nesnesini self
olarak geçirdiği ve orijinal self
tamamen yok sadığından, bu yöntemlerle çalışmaz. _CooldownFunction
nesnesi yerine orijinal self
düzgün şekilde geçirerek, yöntemleri ile çalışmak için nasıl alabilirim?
>>> class Test:
... @cooldown(10)
... def f(self, arg):
... print(self, arg)
...
>>> t = Test()
>>> t.f(5)
<Test object at ...> 5
>>> t.f.remaining = 0
>>> t.f(3) # Almost immediately after previous call
<Test object at ...> 3
:
Ayrıca, bu daha da zor (sadece functools.partial(self.__call__, obj)
falan dönmek için __get__
kullanamazsınız) yapar sinek, kalan süreyi değiştirmek için muktedir kullanıcılar için gereklidir Düzenleme: Her iki yöntem ve işlev için değil, yalnızca yöntemler için çalışmak gerekir.
Düzenleme 2: Başlamak için bu tasarımda büyük bir hata var. Normal işlevler için iyi çalışıyor olsa da, her bir örneği ayrı ayrı dekore etmesini istiyorum. Şu anda iki örneğim t1
ve t2
ve t1.f()
'u çağırıyor olsaydım, artık cooldown örnekleri yerine f()
yöntemine eklendiğinden, t2.f()
numaralı telefonu arayabilirdim. Bunun için bir çeşit sözlük kullanabilirdim, ama bu gerçekleşmeden sonra daha da kayboldum ...
Not. Testinizi bir kapamaya kapatıp geri döndürmeyi denediniz mi? – Felk
@Felk 'cooldown' işlevi,' _CooldownFunc' sınıfı değil, dekoratördür. 'Cooldown' dekoratör, orijinal işlevi bir '_CooldownFunc' nesnesiyle değiştirir. Bu nedenle, orijinal işlevi çağırmaya çalıştığınızda, aslında, _cooldownFunc' nesnesinin '__call__' işlevini çağırıyorsunuz. Yani evet, "__call__", süslü işlevi çağırdığınız her zaman çağrılır. Normal işlevler için mükemmel çalışıyor, ancak 'self' parametresi sorun yaratıyor. –
Oh, bunu özledim, üzgünüm. Ama sonra tekrar bir kez 'cooldown' dekoratör şimdi 'func' yaptığı gibi her bir çağırma için yeni bir '_CooldownFunc' örneği yaratmamalı, değil mi? – Felk