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!

~ Read next post in Development ~

FastAPI - SQLAlchemy and Alembic Integration

Posted by Abdullah Caliskan

4 min read