Dekoratörlere Giriş — Manuel ve Nesne Yönelimli Dekoratör Uygulamaları | Python 101
Python serisinin ileri konularından biri olan dekoratörlerin video kaynakçası için hazırlamış olduğum bu doküman/tutorial ile siz de kendi dekoratörlerinizi oluşturarak handler’lar yazabileceksiniz.
Öncelikle dekoratör nedir buna değinmekte fayda var. Her dekoratör aslında bir fonksiyondur. Bu fonksiyonlar @ karakteri ile çağrıldıklarında Python bir fonksiyon tanımı yapmamızı bekler, yani fonksiyon parametre alan fonksiyonlardır.
En basit gösterimi ile bir dekoratörün yapısı;
def decorated(fn: Any):
fn("Mert", 19)
@decorated
def function(name, age):
print(name, age)
"""
Çıktı: Mert 19
"""
şeklindedir.
Buna ek olarak her sınıfın da çağrılabilir (callable) olduğunu göz önüne alarak dekoratör tanımında fonksiyon dışında sınıf da yazabileceğimizi söyleyebiliriz. Mesela “set_attr” diye bir dekoratör yazalım.
Bu dekoratörün amacı kendisine gönderilen sınıfı manipüle ederek ona bir alt nitelik kazandırsın.
Yani;
import typing
def set_attr(fn):
setattr(fn, "print", lambda: print(fn()))
return fn
@set_attr
class MyClass:
id: int = None
name: str = None
def __str__(self):
"MyClass sınıfının pointerının (self) __class__ alt niteliğinin __name__ alt niteliğini geri döndürür. "
return self.__class__.__name__
MyClass.print()
"""
Çıktı: MyClass
"""
Sınıflarla ve fonksiyon tanımları ile dekoratör yazmayı öğrendiysek eğer şimdi daha profesyonel dekoratörler yazmak için adım atalım!
Gördüğünüz üzere burada yazmış olduğumuz dekoratörler verilen işlemler tamamlanınca python uygulamamız sona eriyor. Pekala, handler dediğimiz şey neydi? Aslen yakalama manasına gelen bir sözcük. try-catch gibi. Dene-yakala-kullan.
Handler’lar sürekli bazı işlemleri kontrol ederler, istenen durum oluştuğunda o anki işleme bağlı olarak çeşitli parametrelerle dekoratöre bağımlı tanımlanan fonksiyonları çağırırlar.
Handler yazarken, “on_” ön eki sık kullanılır. Örneğin; “on_event, on_error, on_message, on_closed, on_website_down” tüm bunların manası bir işlemin gerçekleştiğini söylemektedir.
Peki bir handler nasıl yazılır, nesne yönelimli mi yazmalıyız, manuel mi?
Bir handler için bir eylem döngüsü (event-loop) gerekir. Eylem döngüsü için asyncio kullanırken, sürekli kontrol sağlamak için ise while döngüsünü kullanacağız. Bu durumda dekoratörümüze bağımlı fonksiyonları depolamak için ise bir liste oluşturmalıyız.
Yani elimizde “Decorated” isimli bir sınıf olsun, bu sınıf bir liste alt niteliğine (attribute ) sahip olacak.
Bir adet işçi fonksiyona yani istenen durumların sağlanıp sağlanmadığını belirlediğimiz bir worker fonksiyonu.
Aynı şekilde bir adet de gelen fonksiyonları depolayacak asıl dekoratör fonskiyonu tanımlamalıyız.
Hemen başlayalım;
import typing, requests, asyncio
class Coin:
def __init__(self, symbol: str, price: int | float):
self.symbol: str = symbol
self.price: int | float = price
class Decorated:
def __init__(self, url: str = "https://api.binance.com/api/v3/ticker/price"):
self.url: str = url
self.funcs: list = list()
self.delay: int = 30
self.loop: bool = True
self.total: int = 1
async def worker(self):
try:
while self.loop:
r = requests.get(self.url)
prices = r.json()
for p in prices:
symbol, price = p["symbol"], p["price"]
if symbol.endswith("BTC"):
for func in self.funcs:
coin = Coin(symbol=symbol, price=price)
if asyncio.iscoroutinefunction(func):
await func(coin)
else:
func(coin)
print("All coins checked for %d times." % self.total)
self.total += 1
await asyncio.sleep(self.delay)
except Exception as e:
print("Error: %s" % e)
await asyncio.sleep(2)
await self.worker()
def on_btc(self, func: typing.Callable):
self.funcs.append(func)
def run(self):
loop = asyncio.get_event_loop()
return loop.run_until_complete(self.worker())
handler = Decorated()
@handler.on_btc
def function(coin: Coin):
print("%s: %s" % (coin.symbol, coin.price))
handler.run()
devam edecek, işlerim var.