Duraklayan Bir Fonksiyon
Bir generator normal bir fonksiyon gibi görünür ama tüm sonucu hesaplayıp döndürmek yerine değerleri birer birer yield eder, her yield'in arasında, soran kişi bir sonraki değeri isteyene kadar duraklar.
Mümkün olan en basit örnek:
return yerine yield'e dikkat et. for ilk değeri istediğinde Python fonksiyon gövdesini yield 1'e çarpana kadar çalıştırır. Fonksiyon tam orada duraklar, 1'i döngüye geri verir ve tam olarak nerede durduğunu — değişkenler dahil — hatırlar. Sonraki iterasyon, kaldığı yerden devam eder: current += 1, while'a dön, yield 2. Ve döngü koşulu başarısız olana kadar böyle gider; o noktada generator basitçe durur.
Bu durakla-devam et numarası, tüm işin sırrıdır.
Neden Sadece Bir Liste Kurmuyoruz?
Çünkü liste versiyonu tüm değerleri önceden ayırır:
5 öğe için uygun. Şimdi 50 milyon tamsayı istediğini ve yalnızca belirli bir koşulla eşleşen ilkini umursadığını hayal et. Liste versiyonu 50 milyon int ayırır ve sen çoğunu atarsın. Generator versiyonu, çağıranın tükettiği kadar üretir. for döngüsü istediğini bulup break yaptığında generator basitçe durur.
İçselleştirmeye değer kalıp şu: generator'lar, sonucun ne kadarını kullanacağına önceden karar vermeden iterasyon kodu yazmana izin verir.
Generator İfadeleri
Bir liste comprehension yazdıysan söz dizimini zaten biliyorsun — köşeli parantezleri parantezle değiştir:
squares_gen henüz hiçbir şey hesaplamaz. Sadece bir tariftir. Üzerinde gezmek, tarifi adım adım çalıştırır.
Generator ifadeleri, bir iterable tüketen fonksiyonlara argüman olarak mükemmeldir:
Ara liste yok. sum, max ve any, değerleri birer birer okur ki tam olarak istedikleri budur.
Büyük Bir Dosyayı Satır Satır Okumak
Bu, generator'ların kanonik gerçek dünya örneğidir — belleğe sığmayacak kadar büyük bir dosyayı işlemek:
def parse_log_lines(path):
with open(path) as f:
for line in f:
if line.startswith("ERROR"):
yield line.rstrip()
for error in parse_log_lines("app.log"):
print(error)
Dosya tembel okunur. Generator'a her çağrı diskten bir satır çeker, filtreler ve yield eder. Dosya boyutundan bağımsız olarak bellek kullanımı sabit kalır.
Bir Kez ve Tamam
Bir generator'ın tek bir geçişi vardır. Sonuna kadar iterate ettikten sonra tükenir:
İkinci döngü hiçbir şey yazdırmaz. Generator'da bir şey kalmaz.
Birden fazla kez iterate etmen gerekirse, ya taze bir generator için fonksiyonu tekrar çağır ya da diziyi list(...) ile materyalize et ve listeyi tekrar tekrar iterate et. Maliyete göre seç: iş ucuzsa yeniden kurmak uygun; dizi küçükse liste uygun.
next() ve Manuel İterasyon
for döngüsü kullanmak zorunda değilsin. next() değerleri teker teker çeker:
StopIteration, bir generator'ın "işim bitti" sinyali vermesidir. for döngüleri onu sessizce yakalar. Manuel kodda istisnadan kaçınmak için next(gen, default)'a bir varsayılan geçebilirsin.
Sonsuz Generator'lar
Değerler talep üzerine üretildiği için, bir generator sonu olmayan bir diziyi temsil edebilir — tüketici sormayı bıraktığı sürece:
İçinde yield olan while True, programı asmaz — sadece "biri sormaya devam ederse üretmeye devam et" anlamına gelir. Tüketici ne zaman duracağına karar verir.
Bu kalıp, akış verisinde, olay döngülerinde ve tanımlı uzunluğu olmayan bir kaynaktan değer çektiğin her yerde görülür.
yield from: Başka Bir Iterable'a Devretmek
Generator'ın başka bir iterable'daki her değeri yield etmek istiyorsa, yield from bunu tek satırda yapar:
yield from olmadan içinde yield x olan iç içe bir for döngüsü yazardın. Ayrıca hiç kullanırsan send() ve throw() çağrılarını da doğru iletir — ama günlük kod için "bu şeydeki her değeri yield et" olarak düşün.
Generator'a Ne Zaman Uzanmalı
Bir generator'ın doğru araç olduğunu gösteren üç sinyal:
- Dizi büyük, belki sonsuz ya da tamamını üretmek pahalı.
- Tüketici sondan önce durabilir (örneğin, ilk eşleşmede
break). - Ara listeler kurmadan dönüşümleri zincirlemek istiyorsun — filtrele, eşle, al.
Ve ne zaman değil:
- Rastgele erişime (
seq[42]) ihtiyacın var. Generator'lar yalnızca ileri gider. - Aynı diziyi birkaç kez iterate etmen gerekir. Liste kullan.
- Dizi küçük ve zaten elinde. Liste comprehension daha basittir.
Generator'lar, liste comprehension'lar ve düz listeler farklı işler için doğru cevaptır. Beceri, çok düşünmeden birini seçmektir — ve bu sezgiyi geliştirmenin en hızlı yolu, yazdığın her iterasyon için "önce her şeyi üret" mi yoksa "birer birer üret" mi daha iyi oturduğunu fark etmektir.
Sırada: Bağlam Yöneticileri Derinlemesine
Python'un iterasyon için kullandığı deyimlerin çoğunu artık gördün. Bağlam yöneticileri — with deyimi — sırada ve dosya ile ağ bağlantılarından veri akışı için generator'larla iyi eşleşir.
Sıkça Sorulan Sorular
Python'da generator nedir?
Generator, değerleri teker teker üreten ve aralarında duraklayan bir fonksiyondur. Normal bir fonksiyon gibi def ile yazarsın ama return yerine yield kullanırsın. Onu çağırmak bir generator nesnesi döndürür; her for iterasyonu ya da her next() çağrısı, bir sonraki yield'e kadar fonksiyonu çalıştırır.
Bir liste ile generator arasındaki fark nedir?
Bir liste her öğeyi aynı anda bellekte tutar. Bir generator öğeleri talep üzerine hesaplar ve tüketildikten sonra unutur. Büyük ya da sonsuz diziler için generator'lar küçük sabit bir bellek kullanır; tekrar tekrar ihtiyaç duyduğun küçük sonuçlar için liste daha iyidir.
Bir generator'ı iki kez iterate edebilir miyim?
Hayır. Bir generator, ilk tam geçişten sonra tükenir — üzerinde ikinci bir for döngüsü hiçbir şey üretmez. Birden fazla kez iterate etmen gerekirse, ya taze bir generator için fonksiyonu tekrar çağır ya da sonuçları bir listeye materyalize et.