Tarihçe
Python, 2.4 sürümünden itibaren, liste kapsamlarını (list comprehensions) "jeneratör ifadeleri" ile genişletmiştir. Bu, jeneratörlere benzer şekilde tembel bir değer dizisi oluşturmaya olanak tanır, ancak daha kompakt ve okunabilir bir biçimde.
Sorun
Liste kapsamları ([x for x in iterable]) tüm öğeleri hemen belleğe yükleyerek bir liste oluşturur. Bu, öğe sayısı çok fazla olduğunda etkisiz veya tehlikeli olabilir. Jeneratör fonksiyonları (yield kullanarak) daha esnektir, ancak ayrı bir fonksiyon tanımlamasını ve daha fazla kod satırı gerektirir.
Çözüm
Jeneratör ifadeleri ((x for x in iterable)) tembel diziler üretmek için kısa bir sözdizimi sunar (öğeler gerektiği ölçüde hesaplanır, hepsi birden yüklenmez). Liste kapsamlarına benzer görünürler ama parantez kullanır:
# Liste kapsamı her şeyi belleğe yükler squares_list = [x**2 for x in range(10**6)] # Jeneratör ifadesi: öğeler talep edildiğinde gelir, bellek neredeyse kullanılmaz squares_gen = (x**2 for x in range(10**6)) # Jeneratörden ilk beş değeri almak for _ in range(5): print(next(squares_gen))
Anahtar özellikler:
Aynı jeneratör ifadesinden birden çok kez "geçebilir miyiz"?
Hayır, bir kez yineleme yaptıktan sonra jeneratör "tükenir". Yeniden geçiş yapmak için yeni bir jeneratör oluşturmanız veya liste kapsamını kullanmanız gerekir.
it = (x for x in range(3)) print(list(it)) # [0,1,2] print(list(it)) # [] — artık değerler elde edilemez
Jeneratörler kullanımlar arasında durumlarını korur mu?
Evet, jeneratör ifadesi next() (veya her tekrar sırasında) arasında "pozisyonunu" korur, ancak yeni bir nesne oluşturmadıkça başa döndürülemez.
Bir jeneratör ifadesini bir satırda birden çok kez kullanmak mümkün mü?
Hayır! Eğer jeneratörü aynı anda birden çok yere "paketlerseniz" (örneğin, birden çok fonksiyona eşzamanlı olarak, listenin geri dönmesini sağlamadan), bazı veriler kaybolur — her alt kullanım işaretçiyi ileri taşır.
g = (x for x in range(3)) print(sum(g), list(g)) # sum(g) tümünü alır, list(g) boş kalır
Büyük dosyaların analizi için projede kullanıldı:
data = (parse_line(line) for line in file) process(list(data)) other_process(list(data))
Artılar:
Eksiler:
Verilerin yeniden kullanımının gerektiği durumlarda liste kapsamı kullanıldı veya tek seferlik tüketim için bir jeneratör oluşturuldu:
# Jeneratör sadece bir kez analiz için (örneğin, toplamı hesaplamak) total = sum(parse_line(line) for line in file)
Artılar:
Eksiler: