Decorator'lar, Python'da fonksiyonları veya sınıfları değiştirmeden onlara yeni davranışlar eklemenin bir yoludur. Decorator'lar, bir fonksiyonu alır, ona bazı işlevler ekler ve yeni bir fonksiyon döndürür. Bu, "fonksiyon sarmalama" olarak da bilinir.
Decorator'ların temel kullanım amacı, mevcut kodu değiştirmeden yeni işlevler eklemektir. Örneğin, bir fonksiyonun çalışma süresini ölçmek, giriş parametrelerini loglamak veya yetki kontrolü yapmak için decorator'lar kullanılabilir.
Python'da decorator'lar @ sembolü ile kullanılır. Bir decorator fonksiyonu, başka bir fonksiyonu parametre olarak alır ve genellikle bir wrapper fonksiyonu döndürür. Wrapper fonksiyonu, orijinal fonksiyonu çağırmadan önce ve/veya sonra ek işlemler yapar.
# Basit bir decorator örneği
def my_decorator(func):
def wrapper():
print("Fonksiyondan önce yapılacak işlemler")
func()
print("Fonksiyondan sonra yapılacak işlemler")
return wrapper
@my_decorator
def say_hello():
print("Merhaba!")
say_hello()
Çıktı:
Fonksiyondan önce yapılacak işlemler
Merhaba!
Fonksiyondan sonra yapılacak işlemler
Jeneratörler, Python'da iterator oluşturmanın bellekte verimli bir yoludur. Normal fonksiyonlar bir değer döndürür ve sonlanırken, jeneratörler yield ifadesi kullanarak çalışmayı duraklatabilir ve devam ettirebilirler. Bu özellik, özellikle büyük veri kümeleriyle çalışırken bellek verimliliği sağlar.
Jeneratörler, bir seferde tüm öğeleri bellekte tutmak yerine, her seferinde bir öğe üretirler. Bu, özellikle sonsuz seriler veya çok büyük dosyalar üzerinde çalışırken kullanışlıdır. Jeneratörler, next() fonksiyonu ile veya for döngülerinde kullanılabilir.
Yield ifadesi, bir fonksiyonu jeneratör fonksiyonuna dönüştürür. Fonksiyon çağrıldığında bir jeneratör nesnesi döndürür, ancak fonksiyonun kodu hemen çalışmaz. Fonksiyonun kodu, next() çağrıldığında bir sonraki yield ifadesine kadar çalışır ve yield'den sonraki değer döndürülür. Sonraki next() çağrısında, fonksiyon kaldığı yerden devam eder.
# Jeneratör fonksiyonu örneği
def fibonacci_sequence(limit):
a, b = 0, 1
while a < limit:
yield a
a, b = b, a + b
# Jeneratör kullanımı
fib = fibonacci_sequence(100)
for num in fib:
print(num, end=' ')
# Çıktı: 0 1 1 2 3 5 8 13 21 34 55 89
Çıktı:
0 1 1 2 3 5 8 13 21 34 55 89
Context manager'lar, Python'da kaynak yönetimi için kullanılan bir araçtır. with deyimi ile birlikte kullanılırlar ve bir bloğun girişinde ve çıkışında belirli işlemlerin yapılmasını sağlarlar. Bu, özellikle dosya işlemleri, veritabanı bağlantıları veya kilitler gibi kaynakların doğru şekilde açılıp kapatılması için idealdir.
Context manager'lar iki temel metod içerir: __enter__() ve __exit__(). with bloğuna girildiğinde __enter__() metodu çalışır ve genellikle kullanılacak kaynağı döndürür. with bloğundan çıkıldığında ise __exit__() metodu çalışır ve kaynakların temizlenmesi, hataların işlenmesi gibi işlemler yapılır.
Python'da context manager oluşturmanın iki yolu vardır: sınıf tabanlı (__enter__ ve __exit__ metodlarıyla) veya contextlib modülündeki @contextmanager decorator'ı kullanarak. contextlib modülü, jeneratör tabanlı context manager'lar oluşturmak için kullanışlı araçlar sağlar.
# Context manager örneği (dosya işlemleri)
with open('dosya.txt', 'r') as file:
content = file.read()
print(content)
# Burada dosya otomatik olarak kapatılır, hata olsa bile
# Kendi context manager'ımızı yazalım
class Timer:
def __enter__(self):
import time
self.start = time.time()
return self
def __exit__(self, exc_type, exc_val, exc_tb):
import time
self.end = time.time()
print(f"Geçen süre: {self.end - self.start:.2f} saniye")
with Timer():
# Zamanını ölçmek istediğimiz kodlar
sum(range(1000000))
Çıktı:
Geçen süre: 0.04 saniye
Metaprogramming, programların diğer programları veya kendilerini manipüle etme yeteneğidir. Python'da bu, özellikle sınıfların ve nesnelerin çalışma zamanında dinamik olarak oluşturulması ve değiştirilmesi anlamına gelir. Metaclass'lar, sınıfların nasıl oluşturulacağını kontrol eden "sınıfın sınıfı"dır.
Python'da her sınıfın bir metaclass'ı vardır. Varsayılan metaclass type'dır. Özel bir metaclass oluşturarak, sınıf oluşturma sürecini özelleştirebiliriz. Bu, API tasarımında, ORM'lerde (Object-Relational Mapping) ve framework'lerde sıkça kullanılan güçlü bir özelliktir.
Metaclass'lar __new__ ve __init__ metodlarını kullanarak sınıf oluşturma sürecini kontrol eder. __new__ metodu sınıfın oluşturulmasından, __init__ metodu ise sınıfın başlatılmasından sorumludur. Metaclass'lar, sınıf niteliklerini, metodlarını ve hatta miras alınan sınıfları dinamik olarak değiştirebilir.
# Basit bir metaclass örneği
class Meta(type):
def __new__(cls, name, bases, namespace):
# Tüm metod isimlerini büyük harfe çevirelim
uppercase_attrs = {
attr.upper(): val for attr, val in namespace.items()
if not attr.startswith('__')
}
return super().__new__(cls, name, bases, uppercase_attrs)
class MyClass(metaclass=Meta):
def my_method(self):
print("Normal metod")
obj = MyClass()
obj.MY_METHOD() # Metod ismi büyük harfe çevrildi
Çıktı:
Normal metod
Asenkron programlama, Python'da eşzamanlı görevleri yönetmek için kullanılan bir paradigmadır. async ve await anahtar kelimeleri ile uygulanır. Asenkron programlama, özellikle I/O bağlantılı (ağ istekleri, dosya okuma/yazma gibi) işlemlerde performans artışı sağlar.
Asenkron fonksiyonlar, coroutine olarak adlandırılır ve normal fonksiyonlardan farklı olarak çalışır. Bir coroutine çağrıldığında hemen çalışmaz, bunun yerine bir coroutine nesnesi döndürür. Bu nesne, event loop tarafından yönetilir ve await ifadesi ile çalıştırılır.
Python'da asenkron programlama için asyncio modülü kullanılır. Bu modül, event loop'u yönetmek ve coroutine'leri çalıştırmak için gerekli araçları sağlar. Asenkron programlamada, bir coroutine başka bir coroutine'i await ile beklerken, event loop diğer görevleri çalıştırmaya devam edebilir.
# Asenkron programlama örneği
import asyncio
async def fetch_data():
print("Veri alınıyor...")
await asyncio.sleep(2) # Simüle edilmiş I/O işlemi
print("Veri alındı")
return {"data": 123}
async def main():
print("Program başladı")
task = asyncio.create_task(fetch_data())
print("Diğer işler yapılıyor...")
await asyncio.sleep(1)
print("Diğer işler tamamlandı")
data = await task
print(f"Alınan veri: {data}")
asyncio.run(main())
Çıktı:
Program başladı
Veri alınıyor...
Diğer işler yapılıyor...
Diğer işler tamamlandı
Veri alındı
Alınan veri: {'data': 123}
Tebrikler, 15 günlük Python yolculuğunuzu tamamladınız. Aşağıdaki final sınavı ile kendinizi test edebilirsiniz.
Sonucunuz burada görünecektir.