Python Generator ve yield: Veriyi Bellekte Biriktirmeden İşlemek
Generator, değerleri baştan listeye doldurmadan istendikçe üreten fonksiyondur. Rehber yield'in nasıl çalıştığını, dosya akışı örneğini, iteratör ve liste ile farkı ve sık hataları anlatır.
Bir bakışta
- Generator, değerleri istendikçe üretir. Liste gibi hepsini önceden hesaplayıp saklamaz; bellekte yalnızca işlenen öğe durur.
- İçinde yield olan fonksiyon generator fonksiyonudur. Çağırınca gövde çalışmaz, bir generator nesnesi döner.
- yield durumu askıya alır, return bitirir. Generator sonraki istekte yerel değişkenleriyle kaldığı yerden devam eder.
- Generator tek kullanımlıktır. Tükendikten sonra yeniden dolaşılırsa boş gelir; yeniden çağırmak gerekir.
- Liste ile generator arasında seçim kullanıma bağlıdır. Birden fazla erişim için liste, bir kez geçilecek büyük akış için generator uygundur.
Liste yerine akış: generator neyi çözer
Büyük bir günlük dosyasındaki satırları işleyen ya da bir veritabanından kayıtları parça parça çeken bir kod yazdığınızı düşünün. Tüm sonuçları önce bir listeye doldurup sonra işlerseniz, bütün veri aynı anda belleğe yüklenir. Veri büyüdükçe bu yaklaşım yavaşlar, sonunda bellek yetmez.
Generator bu soruna farklı bakar: değerleri baştan hesaplayıp saklamaz, istendikçe üretir. Python'un kendi nasıl-yapılır belgesi de aynı ayrımı yapar: normal fonksiyon bir değer hesaplayıp döndürür, generator ise bir değer akışı döndüren bir iteratördür. Bir sonraki değer ancak döngü onu istediğinde hesaplanır.
Bu "ihtiyaç anında üretme" davranışına tembel değerlendirme denir. Sonuç olarak aynı anda bellekte yalnızca işlenen öğe durur; kaynak büyük de olsa kod aynı kalır. Üstelik sonsuz bir akışı bile temsil edebilirsiniz, çünkü bir liste gibi bitmesi gerekmez.
Generator, iteratörü yazmanın kısa yoludur
Üç düzey, her biri bir öncekine dayanır
- IterableÜzerinde for ile dolaşılabilen nesne: liste, metin, dosya
- IteratorSıradaki değeri next ile veren, nerede kaldığını hatırlayan nesne
- Generatorİçinde yield bulunan fonksiyonun döndürdüğü, durumu kendi tutan iteratör
yield ile ilk generator fonksiyonu
Generator yazmak için ayrı bir sınıf gerekmez: bir fonksiyonun içinde return yerine yield kullanmanız yeterlidir. Belgelere göre içinde yield anahtar sözcüğü bulunan her fonksiyon bir generator fonksiyonudur ve Python bunu derlerken özel işler.
def tek_tek_say(sinir): sayac = baslangic while sayac < sinir: yield sayac sayac += artis
for deger in tek_tek_say(sinir_degeri): print(deger)
Fonksiyonu çağırdığınızda gövde çalışmaz; size bir generator nesnesi verilir. Gövde ancak for döngüsü ya da next çağrısı değer isteyince, ilk yield satırına kadar çalışır. Değer döngüye verilir, fonksiyon orada durur. Bir sonraki istekte kaldığı yerden, yerel değişkenleri korunmuş olarak devam eder.
Belgelerin ifadesiyle yield ile return arasındaki büyük fark şudur: yield'e ulaşıldığında yürütme durumu askıya alınır ve yerel değişkenler saklanır. return ise fonksiyonu bitirir ve yerel durumu atar. Fonksiyonun sonuna gelindiğinde ya da return çalıştığında akış sona erer; elle next çağırıyorsanız bu noktada StopIteration alırsınız, for döngüsü ise bunu sessizce yakalayıp bitirir.
Generator her istekte kaldığı yerden devam eder
Döngü her yeni değer isteğinde tekrar eder
- next ya da for istek yaparDöngü sıradaki değeri ister
- Gövde çalışırKod, bir sonraki yield satırına kadar ilerler
- yield değer verirDeğer çağırana gider
- Durum askıya alınırYerel değişkenler saklanır, fonksiyon bekler
Dosya ve veri akışında örnek kullanım
Gerçek bir iş örneği: bir satış raporu dosyasında yalnızca hatalı kayıtları ayıklayıp işlemek istiyorsunuz. Dosya büyük olabilir, bu yüzden tümünü belleğe almadan satır satır ilerlemek istersiniz.
def hatali_satirlar(dosya_yolu): with open(dosya_yolu, encoding=kodlama) as dosya: for satir in dosya: if 'HATA' in satir: yield satir.strip()
for satir in hatali_satirlar(rapor_yolu): isle(satir)
Burada dosya nesnesi de bir iterable olduğundan her turda yalnızca bir satır okunur. Generator yalnızca hatalı olanları dışarı verir; dosya with bloğu sayesinde akış bitince kapanır.
Generator'lar birbirine bağlanabilir. Bir generator'ın çıktısını başka bir generator'a girdi vermek, her aşamada tek öğe tutan bir işlem hattı kurar: önce satırları oku, sonra süz, sonra dönüştür. Her aşama yalnızca bir sonrakinin istediği kadar çalışır, bu yüzden hiçbir aşama tüm veriyi toplamaz.
Bir diğer yaygın kullanım, veritabanından ya da bir servisten sayfa sayfa veri çekmektir: her sayfa yield ile verilir, çağıran taraf döngüsünde sayfa sınırını hiç düşünmez.
Generator ifadesi ve liste üreteciyle farkı
Python'da kısa generator yazmanın bir de ifade biçimi vardır. Liste üretecinin köşeli parantezi yerine yuvarlak parantez kullanırsınız. Belgeler bunu şöyle anlatır: generator ifadesi, değerleri gerektiğinde hesaplayan ve hepsini aynı anda oluşturmayan bir iteratör döndürür.
kareler_liste = [x * x for x in kaynak] kareler_akis = (x * x for x in kaynak)
toplam = sum(x * x for x in kaynak)
İlk satır tüm sonuçları hemen bir liste olarak üretir ve bellekte tutar. İkinci satır ise yalnızca bir generator nesnesi üretir. sum gibi işlevlere doğrudan generator ifadesi verildiğinde ayrıca parantez yazmanız bile gerekmez.
Seçim ölçütü basit: sonuçlara birden fazla kez bakacaksanız, indeksle erişecekseniz ya da uzunluğunu bilmek isteyecekseniz liste; bir kez dolaşıp geçecekseniz ve veri büyük olabilirse generator. Sonuçların yalnızca bir kısmı lazımsa da generator kazandırır, çünkü kullanılmayan değerler hiç hesaplanmaz.
Liste mi generator mı: sonuçları kaç kez kullanacağınıza bağlı
- Birden fazla kez dolaşılacak ya da indekslenecekListeGenerator bir kez tüketilince biter.
- Bir kez dolaşılacak, veri büyük olabilirGeneratorAynı anda belleğe yalnızca işlenen öğe girer.
- Yalnızca ilk eşleşme gerekiyorGeneratorKullanılmayan değerler hiç hesaplanmaz.
Sık yapılan hatalar
En sık hata, generator'ın tek kullanımlık olduğunu unutmaktır. Bir generator tükendiğinde aynı nesneyi yeniden dolaşırsanız hiçbir değer gelmez ve hata da çıkmaz. Yeniden gerekirse generator fonksiyonunu yeniden çağırarak yeni bir nesne alın.
Diğer tuzaklar:
- Generator'ı liste gibi kullanmak: uzunluk almak için len, indeksle erişmek için köşeli parantez generator nesnesinde çalışmaz; gerekirse list ile listeye çevirin ama bunun belleği yeniden doldurduğunu bilin.
- yield yerine return yazıp fonksiyonu sıradan fonksiyona çevirmek: yield içermeyen fonksiyon artık generator değildir.
- Generator içinde açılan kaynakları kapatmamak: dosya ya da bağlantı için with kullanın, böylece akış yarıda bırakılsa da kaynak serbest kalır.
- Hata ayıklarken generator'ı print ile yazdırmak: yalnızca nesnenin adresini görürsünüz, değerleri görmek için dolaşmanız gerekir.
- Gereksiz yere generator seçmek: küçük ve birden fazla kez kullanılan verilerde liste daha sade ve daha kolay hata ayıklanır.
Generator'a dışarıdan değer göndermek de mümkündür; belgelerin anlattığı send yöntemiyle yield bir ifade gibi değer alır. Ancak bu ileri bir kullanımdır ve çoğu veri işleme işinde yield ile değer üretmek yeterlidir.
Bu beceriyi Mindset programlarında nerede çalışırsınız
Python ile Programlama Eğitimi Fonksiyonlar modülünü, Data Science Bootcamp ise Fonksiyonlar ve Dosyalar ile Çalışmak modüllerini içerir; generator bu iki başlığın kesiştiği, büyük dosya ve veri akışlarıyla çalışan analist ve geliştiriciye hitap eden bir konudur. Veriyi parça parça işleyen küçük hatlar kurmak isteyen ekipler için fonksiyon bilgisinin doğal devamıdır.
İlgili Mindset programı
Python ile Programlama Eğitimi
Sık sorulan sorular
Generator ile iterator aynı şey midir?
Her generator bir iteratördür ama her iteratör bir generator değildir. İteratör, sıradaki değeri veren ve nerede kaldığını hatırlayan herhangi bir nesnedir; elle sınıf yazarak da kurulabilir. Generator ise aynı işi yield içeren sıradan bir fonksiyonla yapmanızı sağlar ve durumu sizin yerinize saklar.
Generator ne zaman bitmiş olur?
Fonksiyonun gövdesi sona erdiğinde ya da bir return çalıştığında. Elle next çağırıyorsanız bu noktada StopIteration alırsınız; for döngüsü bu durumu kendisi yakalayıp döngüyü bitirir. Bittikten sonra aynı generator yeni değer üretmez, yeniden başlatmak için fonksiyonu tekrar çağırmanız gerekir.
Generator listeden her zaman hızlı mıdır?
Hayır. Generator'ın ana kazancı bellek kullanımıdır; hız her durumda artmaz. Küçük verilerde ya da sonuçlara birden fazla kez erişirken liste hem basit hem pratiktir. Gerçek bir performans kararı için kendi verinizle ölçüm yapmak gerekir; genel bir hız vaadi doğru olmaz.
Generator içinde return değer döndürebilir mi?
Evet. Python belgelerine göre generator içinde return deger yazmak, StopIteration istisnasının bu değerle oluşturulmasına yol açar. Akış bu noktada biter. Değer for döngüsüyle dolaşırken görünmez, bu yüzden günlük kullanımda genellikle gerek duyulmaz; ileri kullanımda sonucu çağırana iletmek için başvurulur.
Generator ifadesi hangi durumda tercih edilir?
Tek satırlık bir dönüşümü bir kez dolaşacaksanız, özellikle sum, max ya da any gibi bir işleve besleyecekseniz. Liste üretecine göre ara liste oluşmaz. Mantık birkaç adımdan uzunsa okunurluk için normal generator fonksiyonu yazmak daha anlaşılırdır.
İlgili rehberler
İlgili eğitimler
Kategori: Yazılım Geliştirme