Kurs
Dekoratörler, Python’da fonksiyonların ve metotların kodunu değiştirmeden davranışlarını değiştirmenize veya genişletmenize olanak tanıyan güçlü ve zarif bir özelliktir.
Dekoratör, mevcut bir nesneye yapısını değiştirmeden yeni işlevler eklemeye olanak tanıyan bir Python tasarım kalıbıdır. Genellikle fonksiyonlara uygulanırlar ve fonksiyonların davranışını zenginleştirmede veya değiştirmede kritik bir rol oynarlar. Geleneksel olarak, dekoratörler süslemek istediğiniz fonksiyonun tanımının önüne yerleştirilir. Bu eğitimde, dekoratörleri Python fonksiyonlarında nasıl etkili bir şekilde kullanacağınızı göstereceğiz.
Özet
- Dekoratörler, orijinal kodu değiştirmeden fonksiyon davranışını değiştirmenizi veya genişletmenizi sağlar
- Dekoratörleri temiz bir şekilde uygulamak için
@decorator_namesözdizimini kullanın - Dekoratör yazarken fonksiyon meta verilerini korumak için her zaman
functools.wrapskullanın - Sınıf tabanlı dekoratörler, fonksiyon çağrıları arasında durum tutabilen davranışlar sağlar
- Yaygın kullanım alanları arasında önbellekleme (
@lru_cache), günlük kaydı, kimlik doğrulama ve girdi doğrulama bulunur
Birinci Sınıf Nesneler Olarak Fonksiyonlar
Python’daki fonksiyonlar birinci sınıf vatandaşlardır. Bu, argüman olarak geçirilebilme, bir fonksiyondan döndürülebilme, değiştirilebilme ve bir değişkene atanabilme gibi işlemleri destekledikleri anlamına gelir. Bu özellik, fonksiyonların Python’da diğer nesneler gibi ele alınmasına olanak tanıdığı için programlamada daha fazla esneklik sağlar.
Bu dersteki tüm örnek kodları hızlıca kendiniz çalıştırmak için Python’un önceden kurulu olduğu ve tüm kod örneklerini içeren bir DataLab çalışma kitabını ücretsiz oluşturabilirsiniz. Dekoratörler üzerine daha fazla pratik için bu uygulamalı DataCamp alıştırmasına göz atın.
Fonksiyonları değişkenlere atamak
Başlangıç olarak, her çağrıldığında bir sayıya bir ekleyecek bir fonksiyon yazalım. Daha sonra bu fonksiyonu bir değişkene atayacak ve fonksiyonu bu değişken üzerinden çağıracağız.
def plus_one(number):
return number + 1
add_one = plus_one
add_one(5)6Fonksiyonların içinde başka fonksiyonlar tanımlamak
Fonksiyonların içinde başka fonksiyonlar tanımlamak Python’da güçlü bir özelliktir—ve dekoratörler inşa etmek için gereklidir. Bir diğer temel fikre bakalım: fonksiyonları argüman olarak geçirmek. Bu, bizi dekoratör yazmaya bir adım daha yaklaştıracak.
def plus_one(number):
def add_one(number):
return number + 1
result = add_one(number)
return result
plus_one(4)
5
Fonksiyonları diğer fonksiyonlara argüman olarak geçirmek
Fonksiyonlar, diğer fonksiyonlara parametre olarak da geçirilebilir. Aşağıda bunu örneklendirelim.
def plus_one(number):
return number + 1
def function_call(function):
number_to_add = 5
return function(number_to_add)
function_call(plus_one)
6
Diğer fonksiyonları döndüren fonksiyonlar
Bir fonksiyon, başka bir fonksiyon da üretebilir. Bunu aşağıda bir örnekle göstereceğiz.
def hello_function():
def say_hi():
return "Hi"
return say_hi
hello = hello_function()
hello()
'Hi'
İç Fonksiyonlar ve Kapanımlar
Python, iç içe fonksiyonların kapsayan fonksiyonun dış kapsamına erişmesine izin verir. Bu, dekoratörlerde kapanım (closure) olarak bilinen kritik bir kavramdır.
Python’da kapanım, oluşturulduğu ortamı—o ortam artık etkin olmasa bile—hatırlayan bir fonksiyondur. Bu, bir iç fonksiyonun kapsayıcı kapsamındaki değişkenleri “üzerine kapatabileceği” ve kullanmaya devam edebileceği anlamına gelir.
Dekoratörleri anlamak için kapanımlar esastır; çünkü dekoratörler, içteki sarmalayıcı fonksiyonun kapsayıcı dekoratör fonksiyonunun durumuna erişebilmesine ve bunu değiştirebilmesine dayanır.
Bir kapanım örneği:
def outer_function(message):
def inner_function():
print(f"Message from closure: {message}")
return inner_function
closure_function = outer_function("Hello, closures!")
closure_function()
# Output: Message from closure: Hello, closures!Bu örnekte:
inner_function, kapsayan kapsamındaki bir değişken olanmessage’a eriştiği için bir kapanımdır (outer_function).outer_functionçalışmasını tamamlamış olsa bile,inner_functionmessage’a erişimini sürdürür.
Bir dekoratör oluşturduğunuzda, sarmalayıcı fonksiyon (dekoratörün içindeki) bir kapanımdır. Süslenen fonksiyona ve dekoratör fonksiyonunda tanımlanan ek duruma veya argümanlara erişimini korur. Örneğin:
def simple_decorator(func):
def wrapper():
print("Before the function call")
func()
print("After the function call")
return wrapper
@simple_decorator
def greet():
print("Hello!")
greet()
# Output:
# Before the function call
# Hello!
# After the function callBurada, wrapper bir kapanımdır; greet fonksiyonunu “hatırlar” ve çalıştırılmasından önce ve sonra davranış ekler.
İlk Dekoratörünüzü Oluşturma
Artık kapanımları—bir fonksiyonun kapsayan kapsamdan değişkenleri hatırlama yeteneğini—anladığınıza göre, ilk gerçek dekoratörümüzü oluşturmaya hazırız. Dekoratörlerin perde arkasında çalışmasını sağlayan sihirli unsur kapanımlardır.
Şimdi bir cümleyi büyük harfe dönüştürecek basit bir dekoratör oluşturalım. Bunu, kapalı bir fonksiyonun içinde bir sarmalayıcı tanımlayarak yaparız. Gördüğünüz gibi, daha önce oluşturduğumuz “fonksiyon içinde fonksiyon” yapısına oldukça benzer.
def uppercase_decorator(function):
def wrapper():
func = function()
make_uppercase = func.upper()
return make_uppercase
return wrapper
Dekoratörümüz bir fonksiyonu argüman olarak aldığı için, yeni bir fonksiyon tanımlayıp bunu dekoratöre geçireceğiz. Daha önce bir fonksiyonu bir değişkene atayabileceğimizi öğrenmiştik. Bu numarayı dekoratör fonksiyonumuzu çağırmak için kullanacağız.
def say_hi():
return 'hello there'
decorate = uppercase_decorator(say_hi)
decorate()
'HELLO THERE'
@ sözdizimini kullanmak
Ancak Python, dekoratörleri uygulamamız için çok daha kolay bir yol sağlar. Süslemek istediğimiz fonksiyonun başına @ sembolünü koymamız yeterlidir. Bunu aşağıda pratikte gösterelim.
@uppercase_decorator
def say_hi():
return 'hello there'
say_hi()
'HELLO THERE'
Bir Fonksiyona Birden Fazla Dekoratör Uygulamak
Tek bir dekoratör için @ sözdizimini kullanmaya alıştıktan sonra, işi bir adım ileri götürüp aynı fonksiyon üzerinde birden fazla dekoratörü istifleyebilirsiniz. Yalnız unutmayın: sıra önemlidir!
Aşağıda, cümleyi bir listeye bölen başka bir dekoratör tanımlayacağız. Ardından uppercase_decorator ve split_string dekoratörlerini tek bir fonksiyona uygulayacağız.
import functools
def split_string(function):
@functools.wraps(function)
def wrapper():
func = function()
splitted_string = func.split()
return splitted_string
return wrapper @split_string
@uppercase_decorator
def say_hi():
return 'hello there'
say_hi()
['HELLO', 'THERE']
Yukarıdaki çıktıda, dekoratörlerin alttan üste doğru uygulandığını görüyoruz. Sırayı değiştirsek bir hata görürdük; çünkü listelerin upper niteliği yoktur. Cümle önce büyük harfe çevrilmiş, sonra bir listeye bölünmüştür.
Not: Dekoratörleri istiflerken, orijinal fonksiyonun meta verilerinin istifleme boyunca korunmasını sağlamak için functools.wraps kullanmak yaygın bir uygulamadır. Bu, dekoratif fonksiyonun özelliklerini hata ayıklarken anlamayı ve tutarlılığı korumayı kolaylaştırır.
Dekoratörlerde Argüman Kabul Etmek
Şu ana kadar yalnızca bir fonksiyonu saran dekoratörler gördük. Peki ya dekoratörün kendisini yapılandırmak—örneğin parametre geçirmek—isterseniz? İşte burada dekoratör üreticileri (factory) devreye girer.
def decorator_with_arguments(function):
def wrapper_accepting_arguments(arg1, arg2):
print("My arguments are: {0}, {1}".format(arg1,arg2))
function(arg1, arg2)
return wrapper_accepting_arguments
@decorator_with_arguments
def cities(city_one, city_two):
print("Cities I love are {0} and {1}".format(city_one, city_two))
cities("Nairobi", "Accra")My arguments are: Nairobi, Accra
Cities I love are Nairobi and Accra
Not: Dekoratördeki argüman sayısının (bu örnekte arg1, arg2) sarılan fonksiyondaki argüman sayısıyla (bu örnekte cities) eşleştiğinden emin olmak önemlidir. Bu uyum, hataları önlemek ve argümanlı dekoratörlerde doğru çalışmayı sağlamak için kritik öneme sahiptir.
*args ve **kwargs ile Genel Amaçlı Dekoratörler
Herhangi bir fonksiyona uygulanabilecek genel amaçlı bir dekoratör tanımlamak için *args ve **kwargs kullanırız. *args ve **kwargs, tüm konumsal ve anahtar kelime argümanlarını toplayıp sırasıyla *args ve **kwargs değişkenlerinde saklar. Bu sayede fonksiyon çağrılarında istediğimiz kadar argüman geçebiliriz.
def a_decorator_passing_arbitrary_arguments(function_to_decorate):
def a_wrapper_accepting_arbitrary_arguments(*args,**kwargs):
print('The positional arguments are', args)
print('The keyword arguments are', kwargs)
function_to_decorate(*args)
return a_wrapper_accepting_arbitrary_arguments
@a_decorator_passing_arbitrary_arguments
def function_with_no_argument():
print("No arguments here.")
function_with_no_argument()The positional arguments are ()
The keyword arguments are {}
No arguments here.
Dekoratörü konumsal argümanlarla nasıl kullanacağımızı görelim.
@a_decorator_passing_arbitrary_arguments
def function_with_arguments(a, b, c):
print(a, b, c)
function_with_arguments(1,2,3)
The positional arguments are (1, 2, 3)
The keyword arguments are {}
1 2 3
Bir dekore edilmiş fonksiyona anahtar kelime argümanlarını şöyle geçebilirsiniz:
@a_decorator_passing_arbitrary_arguments
def function_with_keyword_arguments():
print("This has shown keyword arguments")
function_with_keyword_arguments(first_name="Derrick", last_name="Mwiti")
The positional arguments are ()
The keyword arguments are {'first_name': 'Derrick', 'last_name': 'Mwiti'}
This has shown keyword arguments
Not: Dekoratörde **kwargs kullanımı, anahtar kelime argümanlarının işlenmesine olanak tanır. Bu da genel amaçlı dekoratörü esnek kılar ve fonksiyon çağrıları sırasında çeşitli argüman türlerini karşılayabilmesini sağlar.
Argümanları Dekoratörlere Geçirmek
Şimdi de argümanları doğrudan dekoratörün kendisine nasıl geçeceğimizi görelim. Bunu başarmak için, argüman kabul eden bir dekoratör üreticisi tanımlar, ardından onun içinde bir dekoratör tanımlarız. Daha önce olduğu gibi dekoratörün içinde de bir sarmalayıcı fonksiyon tanımlarız.
def decorator_maker_with_arguments(decorator_arg1, decorator_arg2, decorator_arg3):
def decorator(func):
def wrapper(function_arg1, function_arg2, function_arg3) :
"This is the wrapper function"
print("The wrapper can access all the variables\n"
"\t- from the decorator maker: {0} {1} {2}\n"
"\t- from the function call: {3} {4} {5}\n"
"and pass them to the decorated function"
.format(decorator_arg1, decorator_arg2,decorator_arg3,
function_arg1, function_arg2,function_arg3))
return func(function_arg1, function_arg2,function_arg3)
return wrapper
return decorator
pandas = "Pandas"
@decorator_maker_with_arguments(pandas, "Numpy","Scikit-learn")
def decorated_function_with_arguments(function_arg1, function_arg2,function_arg3):
print("This is the decorated function and it only knows about its arguments: {0}"
" {1}" " {2}".format(function_arg1, function_arg2,function_arg3))
decorated_function_with_arguments(pandas, "Science", "Tools")The wrapper can access all the variables
- from the decorator maker: Pandas Numpy Scikit-learn
- from the function call: Pandas Science Tools
and pass them to the decorated function
This is the decorated function, and it only knows about its arguments: Pandas Science Tools
Dekoratörleri Hata Ayıklama
Gördüğümüz gibi dekoratörler fonksiyonları sarar. Orijinal fonksiyon adı, docstring’i ve parametre listesi sarmalayıcı kapanım tarafından gizlenir: Örneğin decorated_function_with_arguments meta verilerine erişmeye çalıştığımızda, sarmalayıcının meta verilerini görürüz. Bu durum, hata ayıklamayı zorlaştırır.
decorated_function_with_arguments.__name__
'wrapper'
decorated_function_with_arguments.__doc__
'This is the wrapper function'
Bu sorunu çözmek için Python functools.wraps adlı bir dekoratör sağlar. Bu dekoratör, süslenmemiş fonksiyondaki kaybolan meta verileri dekore edilmiş kapanıma kopyalar. Bunu nasıl yapacağımızı gösterelim.
import functools
def uppercase_decorator(func):
@functools.wraps(func)
def wrapper():
return func().upper()
return wrapper
@uppercase_decorator
def say_hi():
"This will say hi"
return 'hello there'
say_hi()
'HELLO THERE'
Artık say_hi’nin meta verilerini kontrol ettiğimizde, sarmalayıcıyı değil, orijinal fonksiyonu doğru şekilde yansıttığını görürüz.
say_hi.__name__
'say_hi'
say_hi.__doc__
'This will say hi'
Dekoratör tanımlarken her zaman functools.wraps kullanmak tavsiye edilir ve iyi bir uygulamadır. Hata ayıklamada sizi birçok baş ağrısından kurtarır.
Sınıf Tabanlı Dekoratörler
Fonksiyon tabanlı dekoratörler yaygın olsa da, Python sınıf tabanlı dekoratörler oluşturmanıza da izin verir; bu da özellikle karmaşık kullanım senaryolarında daha fazla esneklik ve sürdürülebilirlik sağlar. Python’da nesne yönelimli programlamaya yeniyseniz, temellere göz atmak bu bölümü daha iyi anlamanıza yardımcı olacaktır. Sınıf tabanlı bir dekoratör, bir fonksiyon gibi davranmasını sağlayan bir __call__ metoduna sahip bir sınıftır.
class UppercaseDecorator:
def __init__(self, function):
self.function = function
def __call__(self, *args, **kwargs):
result = self.function(*args, **kwargs)
return result.upper()
@UppercaseDecorator
def greet():
return "hello there"
print(greet())
# Output: HELLO THERENasıl çalışır:
__init__metodu, dekore edilecek fonksiyonla dekoratörü başlatır.__call__metodu, dekore edilmiş fonksiyon çağrıldığında devreye girer ve dekoratörün davranışı değiştirmesine olanak tanır.
Sınıf tabanlı dekoratörlerin avantajları:
- Durum tutan dekoratörler: Sınıf tabanlı dekoratörler, örnek değişkenlerini kullanarak durum tutabilir; fonksiyon tabanlı dekoratörlerde ise bu, kapanımlar veya global değişkenler gerektirir.
- Okunabilirlik: Karmaşık dekoratörler için mantığı bir sınıf içinde kapsüllemek kodu daha düzenli ve anlaşılır kılabilir.
Durum tutan bir dekoratör örneği:
class CallCounter:
def __init__(self, function):
self.function = function
self.count = 0
def __call__(self, *args, **kwargs):
self.count += 1
print(f"Function {self.function.__name__} has been called {self.count} times.")
return self.function(*args, **kwargs)
@CallCounter
def say_hello():
print("Hello!")
say_hello()
say_hello()
# Output:
# Function say_hello has been called 1 times.
# Hello!
# Function say_hello has been called 2 times.
# Hello!Gerçek Dünya Dekoratör Kullanımı: Önbellekleme
lru_cache dekoratörü, Python’da maliyetli fonksiyon çağrılarının sonuçlarını önbelleğe alan yerleşik bir araçtır. Bu, tekrarlanan girdiler için gereksiz hesaplamalardan kaçınarak performansı artırır.
Örnek:
from functools import lru_cache
@lru_cache(maxsize=128)
def fibonacci(n):
if n < 2:
return n
return fibonacci(n - 1) + fibonacci(n - 2)
print(fibonacci(50)) # Subsequent calls with the same argument are much fasterDekoratörlerin diğer yaygın kullanımları:
Günlük kaydı: Hata ayıklama veya denetim için fonksiyon çağrılarını, argümanları ve dönüş değerlerini izleyin.
Kimlik doğrulama: Flask veya Django gibi web uygulamalarında erişim kontrolü uygulayın.
Çalışma süresi ölçümü: Performans kritik görevlerde fonksiyon çalışma süresini ölçün ve optimize edin.
Yeniden deneme mekanizması: Özellikle ağ işlemlerinde başarısız fonksiyon çağrılarını otomatik olarak yeniden deneyin.
Girdi doğrulama: Çalıştırmadan önce fonksiyon argümanlarını doğrulayın.
Python Dekoratörleri Özeti
Dekoratörler, bir fonksiyonun, metodun veya sınıfın işlevselliğini, alt sınıfları doğrudan kullanmak veya süslenen fonksiyonun kaynak kodunu değiştirmek zorunda kalmadan dinamik olarak değiştirir. Python’da dekoratör kullanmak, kodunuzu TEKRAR ETMEYİN (DRY) ilkesine de uygun hale getirir. Dekoratörlerin aşağıdakiler gibi çeşitli kullanım alanları vardır:
- Flask ve Django gibi Python çatılarında yetkilendirme
- Günlük kaydı
- Çalışma süresini ölçme
- Eşzamanlama
Python dekoratörleri hakkında daha fazla bilgi için Python’un Decorator Library sayfasına göz atın.
FAQs
Dekoratör kullanırken herhangi bir performans hususu var mı?
Evet, dekoratörler ek fonksiyon çağrıları getirdikleri için ek yük oluşturabilir. Performansın kritik olduğu durumlarda, özellikle de dekore edilen fonksiyon sıkça ve performansa duyarlı bir bağlamda çağrılıyorsa, bu ek yükü dikkate almak önemlidir.
Dekoratörler sınıf metotlarıyla da kullanılabilir mi, nasıl?
Evet, dekoratörler sınıf metotlarına da tıpkı normal fonksiyonlar gibi uygulanabilir. Dekoratör, argüman olarak metodu alır ve yeni bir metot veya değiştirilmiş bir sürümünü döndürür. Bu, sıklıkla günlük kaydı, erişim kontrolü veya önkoşulların zorlanması için kullanılır.
Dekoratörler günlükleme amaçları için nasıl kullanılabilir?
Dekoratörler, fonksiyon yürütmesini; çağrıları, argümanları ve dönüş değerlerini bir günlükleme sistemine kaydeden kodla sarmalayarak izlemek için kullanılabilir. Bu, izleme ve hata ayıklamaya yardımcı olur.
Dekoratörlerde @ sembolünün önemi nedir?
Python’daki @ sembolü, bir dekoratörü bir fonksiyona uygulamayı kolaylaştıran sözdizimsel şekerlemedir. Dekoratörü doğrudan fonksiyon tanımının üzerine yazarak kodu daha temiz ve okunabilir hale getirir.
Bir dekoratör bir fonksiyonun dönüş değerini değiştirebilir mi, bu nasıl çalışır?
Evet, bir dekoratör, sarmalayıcı fonksiyonun dönüş ifadesini değiştirerek bir fonksiyonun dönüş değerini değiştirebilir. Örneğin, çıktı veri tipini dönüştürebilir, biçimlendirebilir veya nihai sonucu döndürmeden önce ek işlem yapabilir.
İç içe bir fonksiyon kapsayan fonksiyonun değişkenine eriştiğinde Python değişken kapsamını nasıl ele alır?
Python LEGB (Local, Enclosing, Global, Built-in) kapsam kuralını kullanır. İç içe fonksiyonlar söz konusu olduğunda, içteki fonksiyon kapsayan fonksiyonun kapsamındaki değişkenlere erişebilir; bu da, dıştaki fonksiyon çalışmasını tamamladıktan sonra bile iç fonksiyonun bu değişkenlere erişimini sürdürdüğü kapanımlara olanak tanır.
Kapanım ile dekoratör arasındaki fark nedir?
Kapanım, dış kapsamındaki değişkenleri hatırlayan bir fonksiyondur. Dekoratör ise diğer fonksiyonları sarmak veya geliştirmek için kapanımları kullanan bir fonksiyondur.
functools.wraps’ı ne zaman kullanmalıyım?
Dekoratör yazarken her zaman functools.wraps kullanın. Bu, orijinal fonksiyonun meta verilerini (ad ve docstring gibi) korur; bu da hata ayıklama ve dokümantasyon için faydalıdır.
Dekoratörler argüman kabul edebilir mi?
Evet! Dekoratörleri başka bir fonksiyonla (dekoratör üreticisi) sarmalayarak onlara argüman geçebilirsiniz. Bu, dekoratör davranışını özelleştirmenize olanak tanır.
Sınıf tabanlı dekoratörlerin avantajı nedir?
Sınıf tabanlı dekoratörler, fonksiyon çağrıları arasında durum tutmanıza ve daha karmaşık mantığı nesne yönelimli bir şekilde düzenlemenize olanak tanır.
Dekoratörler yalnızca fonksiyonlar için midir?
Hayır. Dekoratörleri sınıflar ve metotlar üzerinde de kullanabilirsiniz. Flask ve Django gibi çatıların yönlendirmeleri, görünümleri ve modelleri üzerinde yaygın olarak kullanılırlar.