Python Decorators
Decoratorler aslında birer wrapperdır. Fonksiyonun davranışı değiştirmeye yararlar. Fonksiyon çalışmadan önce yapılması gereken bir şey varsa veya fonksiyonları dinamik olarak bir yerlere kayıt etmek istiyorsanız decoratorler bu iş için biçilmiş kaftandır.
Python Decorator’leri ne değildir?
Python decoratorlerin görevi, sınıfı veya fonksiyonu sarmak, işlevlerini değiştirmektir. The GoF (Gang of Four) tasarım kalıplarından, decorator design pattern ile bir bağı yoktur. Decorator design pattern’da amaç bir nesnenin davranışını değiştirmek için onu “decorate” eder. Ve bu işlemi, decorator sınıfları oluşturarak, undecorated base class’lara uygular.
Decorator design pattern için.
Decorator Design Pattern ve Python Decorators
Her ikisi de aynı işlevleri eklemek için birden fazla kez sınıfa/fonksiyona uygulanabilir. Ancak decorator tasarım kalıbı decorate edeceğiniz sınıflar ile tasarlanmasını gerektirir.
Python decoratorleri, işlevlerini herhangi bir sınıfın herhangi bir fonksiyonu üzerinde yaparlar.
Python Decorator Örnekleri
Python’da decorator fonksiyonları yazabilmemiz için gerekli bir konsept vardır. First-class everything konsepti.
def say_hello_decorator(func):
def wrapper():
print("Hello ", end="")
func()
return wrapper
First-class everything konseptinde, nesneler değişkene atanabilir, başka nesneler içinde saklanabilir, parametre olabilir ve geri dönüş değeri olabilirler. Decoratorlerin yaptığı şey, decorator fonksiyonuna parametre olarak bir fonksiyon alınır. Bu fonksiyon, başka bir fonksiyon içinde çağırılır ancak, ekstra işlevler ile birlikte. Daha sonra decorate edilmiş fonksiyon geri döndürülür.
class Person(object):
def __init__(self, name):
self.__name = name
def print_name(self):
print(self.__name)
Person sınıfının print_name fonksiyonuna decoratorler yardımıyla ekstra özellikler ekleyeceğiz. say_hello_decorator ile, “Hello abdullah” yazdırmaya çalışalım.
if __name__ == '__main__':
abdullah = Person('abdullah')
decorated_func = say_hello_decorator(abdullah.print_name)
decorated_func()
Ekran çıktısı olarak; “Hello abdullah” göreceğiz. Yukarıda, bound method nesnesini kullandık. abdullah nesnesi ile ilişkilendirilmiş durumda.
Decoratorler için @ sembolü!
class Person(object):
def __init__(self, name):
self.__name = name
@say_hello_decorator
def print_name(self):
print(self.__name)
Burada, örnek metodumuzu decorate etmeye çalıştık. Ancak, bu şekildeyken bu metot çalışmaz. Sebebi, decorator fonksiyonumuzda ki func() çağrısında self parametresinin eksikliği. Bu yüzden decorator fonksiyonumuzu şu şekilde güncellemeliyiz.
def say_hello_decorator(func):
def wrapper(obj):
print("Hello ", end="")
func(obj)
return wrapper
Burada bir fark olarak wrapper fonksiyonuna obj parametresi eklenmiştir. Bu self parametresine karşılık gelmektedir ve func(), fonksiyonunu unbound method olarak kullanmamızı sağlamıştır.
“@functools.wraps” Ne İşe Yarar?
wraps’te bir decoratordür. wraps kullanmadan da decorator oluşturabilirsiniz.
Ancak kullanmanız durumunda, decorate ettiğiniz dökümanın docstring’ini kaybetmemiş olursunuz. Py3Doc
Flask Route Decorator
Flask’ı hepimiz duymuşuzdur. Python micro web framework. Route decoratoru ile, istek yapılan URI’ya göre hangi fonksiyonun/resource’un işletileceğini belirtiyoruz. Örnek olarak basit bir route decoratoru yazalım.
class App(object):
def __init__(self):
route_functions = {}
def route(self, url_pattern):
def wrapper(f):
self.route_functions[url_pattern] = f
return f
return wrapper
app = App()
@app.route("/")
def index():
return "Hello"
print(app.route_functions)
“route” metodu bizim decorator fonksiyonumuz. “url_pattern” parametresi alıyor ve bunu “route_functions[url_pattern]” olarak decorate edilmiş fonksiyonu kayıt ediyor. Burada dikkat edilmesi gereken husus, daha yaptığımız örneklerden farkı decorate edilecek olan fonksiyon wrapper fonksiyonuna parametre olarak gönderilmiş. Decorate işleminin açık hâli aslında şöyledir.
app = App()
def index():
return "Hello"
app.route("/")(index)
app.route fonksiyonundan bize wrapper fonksiyonu dönüyor ve bizden wrap edeceği fonksiyonu bekliyor. Biz de wrap edeceği fonksiyonu parametre olarak geçiriyoruz ve route_functions dict’ine eklemiş oluyoruz.
Flask Login Required Decorator
Bir başka çok kullanılan decorator ise, login_required decoratorüdür. Bu decoratörün amacı, decarote ettiği endpoint’e sadece kimliği doğrulanmış kullanıcıların erişmesi için kullanılır.
from flask import Flask, g, request, url_for, redirect
from functools import wraps
def login_required(f):
@wraps(f)
def wrapper():
if g.user is None:
return redirect(url_for('login', next=request.url))
return f()
return wrapper
app = Flask(__name__)
@app.route("/hidden/resource')
@login_required
def hidden():
return "Hidden resource"
“login_required” decoratoründe, kullanıcının g application context’inde olup olmadığı kontrol ediliyor ve kullanıcı tanımlı değilse, login sayfasına yönlendiriliyor. Kullanıcı tanımlı ise, wrap edilmiş olan fonksiyonumuz işletiliyor.
Bir diğer husus ise, decoratorler arka arkaya zincirleme kullanılabilir. Yukarıda ki örnekte, önce route decoratorü, daha sonra da login_required decoratorü kullanılmıştır. Sıralama önemlidir!