Python Generator'ları Nedir?
Özünde, Python generator'ları, bir dizi değeri tembel (lazy) şekilde üreten özel bir fonksiyon türü ya da kompakt bir ifadedir. Generator'ları bir fabrikadaki konveyör bandı gibi düşünün: Tüm ürünleri tek bir yerde istifleyip alanı tüketmek yerine, banttan geldikçe her birini işlersiniz. Bu, generator'ları bellek açısından verimli kılar ve Python’un, for döngüleri ve comprehension’lar gibi çoğu yerleşik aracının temelini oluşturan iterator protokolünün doğal bir uzantısıdır.
Generator'ların arkasındaki sihir yield anahtar sözcüğünde yatar. return'ün tek bir değer döndürüp fonksiyondan çıkmasına karşılık, yield bir değer üretir, fonksiyonun yürütülmesini duraklatır ve durumunu kaydeder. Generator yeniden çağrıldığında kaldığı yerden devam eder.
Örneğin, devasa bir günlük dosyasını satır satır okuduğunuzu düşünün. Bir generator, tüm dosyayı belleğe yüklemeden her satırı okundukça işleyebilir. Bu "tembel değerlendirme" generator'ları geleneksel fonksiyonlardan ayırır ve performansın kritik olduğu görevlerde başvurulan bir araç yapar.
Basit Bir Python Generator Örneği
Fikre alışmak için biraz pratik yapalım. İşte ilk n tamsayıyı üreten bir generator fonksiyonu.
def generate_integers(n):
for i in range(n):
yield i # Pauses here and returns i
# Using the generator
for num in generate_integers(5):
print(num)
0
1
2
3
4
Kaputun altında neler olduğunu görmenize yardımcı olmak için görsel bir şema hazırladım:

Python Generator Söz Dizimi ve Kalıpları
Generator'lar birden fazla şekilde uygulanabilir. Bununla birlikte, iki ana yol vardır: generator fonksiyonları ve generator ifadeleri.
Generator fonksiyonları
Bir generator fonksiyonu, normal bir fonksiyon gibi tanımlanır ancak return yerine yield kullanır. Çağrıldığında, üzerinde yineleme yapılabilen bir generator nesnesi döndürür.
def count_up_to(n):
count = 1
while count <= n:
yield count
count += 1
# Using the generator
counter = count_up_to(5)
for num in counter:
print(num)
1
2
3
4
5
Yukarıdaki örnekten görebileceğimiz gibi, count_up_to fonksiyonu çağrıldığında bir generator nesnesi döndürür. For döngüsü her değer istediğinde, fonksiyon yield'e ulaşana kadar çalışır, count'un mevcut değerini üretir ve yinelemeler arasında durumunu koruyarak tam olarak kaldığı yerden devam edebilir.
Generator ifadeleri
Generator ifadeleri, generator oluşturmanın kompakt bir yoludur. Liste kavramalarına (list comprehension) benzerler ancak köşeli parantezler yerine parantez kullanırlar.
# List comprehension (eager evaluation)
squares_list = [x**2 for x in range(5)] # [0, 1, 4, 9, 16]
# Generator expression (lazy evaluation)
squares_gen = (x**2 for x in range(5))
# Using the generator
for square in squares_gen:
print(square)
0
1
4
9
16
Peki bir liste kavrama ile generator ifadesi arasındaki fark nedir? Liste kavrama, tüm listeyi bellekte oluşturur; generator ifadesi ise değerleri birer birer üretir ve bellek tasarrufu sağlar. Liste kavramalarına aşina değilseniz, Python List Comprehension Eğitimi yazımızı okuyabilirsiniz.
Python generator vs. iterator
Python’daki geleneksel iterator’lar, açık __iter__() ve __next__() metodlarına sahip sınıflar gerektirirdi; bu da çokça kalıp kod ve elle durum yönetimi demekti. Generator fonksiyonları ise durumu otomatik olarak koruyarak ve bu metodlara olan ihtiyacı ortadan kaldırarak süreci basitleştirir—tıpkı n'e kadar her sayının karesini üreten basit bir fonksiyonun gösterdiği gibi.
Neden Python Generator'larını Kullanırız
Python generator'larının ne olduğundan bahsederken, neden kullanıldıklarına dair fikri de biraz aktardım. Bu bölümde biraz daha detaya girmek istiyorum. Çünkü generator'lar sadece havalı bir Python özelliği değildir; gerçekten gerçek sorunları çözerler.
Bellek verimliliği
Listeler veya dizilerin aksine, tüm öğelerini aynı anda bellekte tutmak yerine, generator'lar değerleri anlık olarak üretir; böylece aynı anda yalnızca tek bir öğe bellekte tutulur.
Örneğin, Python 2’nin range() ve xrange() farkını düşünün:
-
range()bellekte bir liste oluştururdu; bu, büyük aralıklar için sorun yaratabilirdi. -
xrange()ise tembel şekilde değer üreten bir generator gibi davranırdı.
xrange()'in davranışı daha kullanışlı olduğundan, artık Python 3’te range() de generator gibi davranır; böylece tüm değerleri aynı anda depolamanın bellek yükünden kaçınılır.
Fikri göstermek için 10 milyon sayı üretirken bellek kullanımını karşılaştıralım:
import sys
# Using a list
numbers_list = [x for x in range(10_000_000)]
print(f"Memory used by list: {sys.getsizeof(numbers_list) / 1_000_000:.2f} MB")
# Using a generator
numbers_gen = (x for x in range(10_000_000))
print(f"Memory used by generator: {sys.getsizeof(numbers_gen)} bytes")
Memory used by list: 89.48 MB
Memory used by the generator: 112 bytes
Gördüğünüz gibi, generator listeye kıyasla neredeyse hiç bellek kullanmıyor ve bu fark kayda değerdir.
Performans iyileştirmeleri
Tembel değerlendirme sayesinde, değerler yalnızca ihtiyaç duyulduğunda hesaplanır. Bu da tüm dizi üretilmesini beklemeden veriyi hemen işlemeye başlayabileceğiniz anlamına gelir.
Örneğin, ilk 1 milyon sayının karelerinin toplamını hesapladığınızı düşünün:
# Using a list (eager evaluation)
sum_of_squares_list = sum([x**2 for x in range(1_000_000)])
# Using a generator (lazy evaluation)
sum_of_squares_gen = sum(x**2 for x in range(1_000_000))
Her iki yaklaşım da aynı sonucu verse de, generator sürümü devasa bir liste oluşturmayı önleyerek daha hızlı sonuca ulaşmamızı sağlar.
Basitlik ve okunabilirlik
Generator'lar, kalıp kodu ortadan kaldırarak iterator’ların uygulanmasını basitleştirir. Sınıf tabanlı bir iterator ile bir generator fonksiyonunu karşılaştıralım:
İşte sınıf tabanlı iterator:
class SquaresIterator:
def __init__(self, n):
self.n = n
self.current = 0
def __iter__(self):
return self
def __next__(self):
if self.current >= self.n:
raise StopIteration
result = self.current ** 2
self.current += 1
return result
# Usage
squares = SquaresIterator(5)
for square in squares:
print(square)
İşte generator fonksiyonu:
def squares_generator(n):
for i in range(n):
yield i ** 2
# Usage
squares = squares_generator(5)
for square in squares:
print(square)
Generator sürümü daha kısa, daha okunaklıdır ve kalıp koda ihtiyaç duymaz. Bu, Python felsefesinin mükemmel bir örneğidir: basit olan daha iyidir.
Sonsuz dizileri yönetme
Son olarak şunu söylemek isterim: Generator'lar, listelerle imkânsız olan sonsuz dizileri temsil etmek için benzersiz şekilde uygundur. Örneğin, Fibonacci dizisini düşünün:
def fibonacci():
a, b = 0, 1
while True:
yield a
a, b = b, a + b
# Usage
fib = fibonacci()
for _ in range(10):
print(next(fib))
0
1
1
2
3
5
8
13
21
34
Bu generator, bellek tükenmeden Fibonacci sayılarını süresiz olarak üretebilir. Diğer örnekler arasında canlı veri akışlarını işlemek veya zaman serisi verileriyle çalışmak yer alır.
İleri Düzey Python Generator Kavramları
Şimdi biraz daha zor fikirlere bakalım. Bu bölümde, generator'ları nasıl birleştireceğimizi ve .send(), .throw() ve .close() gibi özgün generator metodlarını nasıl kullanacağımızı inceleyeceğiz.
Generator'ları zincirlemek
Generator'lar birleştirilebilir. Generator'ları zincirleyerek veriyi modüler biçimde dönüştürebilir, filtreleyebilir ve işleyebilirsiniz.
Diyelim ki sonsuz bir sayı diziniz var; her sayının karesini almak ve tek sonuçları elemek istiyorsunuz:
def infinite_sequence():
num = 0
while True:
yield num
num += 1
def square_numbers(sequence):
for num in sequence:
yield num ** 2
def filter_evens(sequence):
for num in sequence:
if num % 2 == 0:
yield num
# Compose the generators
numbers = infinite_sequence()
squared = square_numbers(numbers)
evens = filter_evens(squared)
# Print the first 10 even squares
for _ in range(10):
print(next(evens))
0
4
16
36
64
100
144
196
256
324
Süreç, infinite_sequence fonksiyonunun süresiz olarak sayılar üretmesi, square_numbers'ın her sayının karesini üretmesi ve ardından filter_evens'in tek sayıları eleyip yalnızca çift kareleri üretmesi şeklinde işler. Associate Python Developer kariyer yolumuz bu tür konulara girer; böylece generator'lar, iterator’lar ve liste kavramaları kullanarak karmaşık hat boru hatlarını (pipeline) nasıl kurup hata ayıklayacağınızı görebilirsiniz.
Özel generator metodları
Generator'lar, iki yönlü iletişim ve kontrollü sonlandırma sağlayan gelişmiş metodlarla birlikte gelir.
send()
.send() metodu, bir generator'a değerler geri iletmenize olanak tanır ve onu bir coroutine'e dönüştürür. Bu, etkileşimli veya durumsal generator'lar oluşturmak için kullanışlıdır.
def accumulator():
total = 0
while True:
value = yield total
if value is not None:
total += value
# Using the generator
acc = accumulator()
next(acc) # Start the generator
print(acc.send(10)) # Output: 10
print(acc.send(5)) # Output: 15
print(acc.send(20)) # Output: 35
Nasıl çalıştığı şöyle:
-
Generator’ı başlatmak için
next(acc)çağrılır. -
Her
.send(value)çağrısı,yieldifadesindekivalue'ya atanacak bir değeri generator’a iletir. -
Generator durumunu (
total) günceller ve yeni sonucu üretir.
throw()
.throw() metodu, generator içinde bir istisna fırlatmanıza olanak tanır; bu, hata yönetimi veya belirli koşulları işaretlemek için yararlı olabilir.
def resilient_generator():
try:
for i in range(5):
yield i
except ValueError:
yield "Error occurred!"
# Using the generator
gen = resilient_generator()
print(next(gen)) # Output: 0
print(next(gen)) # Output: 1
print(gen.throw(ValueError)) # Output: "Error occurred!"
Bu da şöyle çalışır:
-
Generator,
.throw()çağrılana kadar normal şekilde çalışır. -
İstisna generator içinde fırlatılır ve
try-exceptbloğuyla ele alınabilir.
close()
.close() metodu, bir GeneratorExit istisnası fırlatarak generator’ı durdurur. Bu, kaynakları temizlemek veya sonsuz generator'ları durdurmak için kullanışlıdır.
def infinite_counter():
count = 0
try:
while True:
yield count
count += 1
except GeneratorExit:
print("Generator closed!")
# Using the generator
counter = infinite_counter()
print(next(counter)) # Output: 0
print(next(counter)) # Output: 1
counter.close() # Output: "Generator closed!"
Ve şöyle işler:
-
Generator,
.close()çağrılana kadar çalışır. -
GeneratorExitistisnası fırlatılır; bu da generator’ın sonlanmadan önce temizleme yapmasına veya bir mesaj kaydetmesine olanak tanır.
Veri Biliminde Gerçek Dünya Uygulamaları
Umarım generator'ların faydalı olduğunu görmeye başlamışsınızdır. Bu bölümde, kullanım örneklerini öne çıkararak günlük işinizde sizin için nasıl çalıştıklarını hayal etmenizi kolaylaştırmaya çalışacağım.
Büyük veri kümelerini işleme
Veri bilimindeki en yaygın zorluklardan biri, belleğe sığmayacak kadar büyük veri kümeleriyle çalışmaktır. Generator'lar, bu tür verileri satır satır işlemek için bir yol sunar.
Diyelim ki satış verilerini içeren 10 GB’lık bir CSV dosyanız var ve belirli bir bölgeye ait kayıtları filtrelemeniz gerekiyor. Bunu bir generator hattıyla şöyle yapabilirsiniz:
import csv
def read_large_csv(file_path):
""" Generator to read a large CSV file line by line."""
with open(file_path, mode="r") as file:
reader = csv.DictReader(file)
for row in reader:
yield row
def filter_by_region(data, region):
""" Generator to filter rows by a specific region."""
for row in data:
if row["Region"] == region:
yield row
# Generator pipeline
file_path = "sales_data.csv"
region = "North America"
data = read_large_csv(file_path)
filtered_data = filter_by_region(data, region)
# Process the filtered data
for record in filtered_data:
print(record)
Burada olanlar:
-
read_large_csvdosyayı satır satır okur ve her satırı bir sözlük olarak üretir. -
filter_by_region, belirtilen bölgeye göre satırları filtreler. -
Boru hattı veriyi artımlı olarak işler ve bellek taşmasını önler.
Bu yaklaşım, verinin analizden önce temizlenip dönüştürülmesi gereken ETL iş akışları için faydalıdır. Bu tür şeyleri ETL and ELT in Python kursumuzda göreceksiniz.
Akış ve boru hatları
Veri bazen sürekli bir akış olarak gelir. Sensör verileri, canlı yayınlar veya sosyal medya gibi.
Diyelim ki her saniye sıcaklık ölçümü üreten IoT cihazlarıyla çalışıyorsunuz. 10 okumalık kayan bir pencere üzerinden ortalama sıcaklığı hesaplamak istiyorsunuz:
def sensor_data_stream():
"""Simulate an infinite stream of sensor data."""
import random
while True:
yield random.uniform(0, 100) # Simulate sensor data
def sliding_window_average(stream, window_size):
""" Calculate the average over a sliding window of readings."""
window = []
for value in stream:
window.append(value)
if len(window) > window_size:
window.pop(0)
if len(window) == window_size:
yield sum(window) / window_size
# Generator pipeline
sensor_stream = sensor_data_stream()
averages = sliding_window_average(sensor_stream, window_size=10)
# Print the average every second
for avg in averages:
print(f"Average temperature: {avg:.2f}")
Açıklama şöyle:
-
sensor_data_streamsonsuz bir sensör okuma akışını simüle eder. -
sliding_window_average, son 10 okumadan oluşan kayan bir pencereyi tutar ve bunların ortalamasını üretir. -
Boru hattı veriyi gerçek zamanlı işler; bu da izleme ve analitik için idealdir.
Ek kullanım alanları
Generator'lar, veri boyutunun öngörülemediği ya da verinin durmadan geldiği/sonsuz olduğu durumlarda da kullanılır.
Web kazıma
Web siteleri kazınırken genellikle kaç sayfa veya öğeyi işlemeniz gerektiğini bilmezsiniz. Generator'lar bu öngörülemezliği zarifçe yönetmenizi sağlar:
def scrape_website(url):
""" Generator to scrape a website page by page."""
while url:
# Simulate fetching and parsing a page
print(f"Scraping {url}")
data = f"Data from {url}"
yield data
url = get_next_page(url) # Hypothetical function to get the next page
# Usage
scraper = scrape_website("https://example.com/page1")
for data in scraper:
print(data)
Simülasyon görevleri
Simülasyonlarda, örneğin Monte Carlo yöntemleri veya oyun geliştirme gibi alanlarda, generator'lar sonsuz veya dinamik dizileri temsil edebilir:
def monte_carlo_simulation():
""" Generator to simulate random events for Monte Carlo analysis."""
import random
while True:
yield random.random()
# Usage
simulation = monte_carlo_simulation()
for _ in range(10):
print(next(simulation))
Bellek ve Hız Kıyaslamaları
Çalışma biçimleri nedeniyle, generator'lar bellek verimliliğinin kritik olduğu senaryolarda mükemmeldir; ancak (şaşırabilirsiniz) her zaman en hızlı seçenek olmayabilirler. Artılarını ve eksilerini anlamak için generator'ları listelerle karşılaştıralım.
Daha önce, bellek açısından generator'ların listelerden daha iyi olduğunu göstermiştik. Bu, 10 milyon sayılık bir dizi üretirken bellek kullanımını karşılaştırdığımız kısımdı. Şimdi farklı bir şey yapalım: hız karşılaştırması:
import time
# List comprehension
start_time = time.time()
sum([x**2 for x in range(1_000_000)])
print(f"List comprehension time: {time.time() - start_time:.4f} seconds")
# Generator expression
start_time = time.time()
sum(x**2 for x in range(1_000_000))
print(f"Generator expression time: {time.time() - start_time:.4f} seconds")
List comprehension time: 0.1234 seconds
Generator expression time: 0.1456 seconds
Generator bellek tasarrufu sağlasa da, bu örnekte aslında listeden daha yavaştır. Bunun nedeni, bu daha küçük veri kümesinde yürütmenin duraklatılıp yeniden sürdürülmesinin getirdiği ek yüktür.
Küçük veri kümeleri için performans farkı önemsizdir; ancak büyük veri kümelerinde generator'ların bellek tasarrufu, küçük hız cezasından genellikle daha ağır basar.
Karşılaşılan Sorunlar
Son olarak, bazı yaygın hatalara veya sorunlara bakalım:
Generator'lar tüketilebilir
Bir generator tükendiğinde yeniden kullanılamaz. Yeniden yinelemek isterseniz onu yeniden oluşturmanız gerekir.
gen = (x for x in range(5))
print(list(gen)) # Output: [0, 1, 2, 3, 4]
print(list(gen)) # Output: [] (the generator is exhausted)
Tembel değerlendirme yanıltıcı olabilir
Generator'lar değerleri talep üzerine ürettiği için, hatalar veya yan etkiler generator üzerinde yineleme yapılana kadar ortaya çıkmayabilir.
Generator'ları aşırı kullanabilirsiniz
Küçük veri kümeleri veya basit görevler için, generator kullanmanın ek yükü, bellek tasarrufuna değmeyebilir. Birden çok yineleme için veriyi somutlaştırdığım şu örneği düşünün.
# Generator expression
gen = (x**2 for x in range(10))
# Materialize into a list
squares = list(gen)
# Reuse the list
print(sum(squares)) # Output: 285
print(max(squares)) # Output: 81
Generator Kullanımını Ne Zaman Seçmeli
Özetlemek için, generator’ları ne zaman kullanacağınıza dair çok genel bazı kurallar vereyim. Şunlar için kullanın:
- Büyük veri kümeleri: Belleğe sığmayacak kadar büyük veri kümeleriyle çalışırken generator kullanın.
- Sonsuz diziler: Canlı veri akışları veya simülasyonlar gibi sonsuz dizileri temsil etmek için generator kullanın.
- Boru hatları: Veriyi adım adım dönüştürüp filtreleyen modüler veri işleme hatları kurmak için generator kullanın.
Bunun yerine veriyi somutlaştırmanızın (listeye dönüştürmeniz) gerektiği durumlar
- Küçük veri kümeleri: Bellek sorun değilse ve tüm öğelere hızlıca erişmeniz gerekiyorsa generator kullanmayın; bunun yerine liste kullanın.
- Birden çok yineleme: Aynı veriyi birden çok kez yinelemeniz gerekiyorsa generator kullanmayın; generator’ı yeniden oluşturmamak için veriyi bir listeye somutlaştırın.
Sonuç ve Önemli Noktalar
Bu makale boyunca, generator'ların büyük veri kümelerini işlemekten gerçek zamanlı veri hatları kurmaya kadar veri bilimindeki gerçek dünya zorluklarıyla başa çıkmanıza nasıl yardımcı olabileceğini inceledik. Pratik yapmaya devam edin. Generator'ları ustalaşmanın en iyi yolu onları kendi çalışmalarınızda kullanmaktır. Başlangıç olarak bir liste kavramasını generator ifadesiyle değiştirmeyi veya bir döngüyü bir generator fonksiyonuna dönüştürmeyi deneyin.
Temel bilgilere hâkim olduktan sonra, generator kavramını temel alan yeni ve daha ileri konuları keşfedebilirsiniz:
-
Coroutine’ler: İki yönlü iletişimi etkinleştirerek veri alıp işleyebilen generator’lar oluşturmak için
.send()ve.throw()kullanın. -
Eş zamansız programlama: Generator'ları Python’un asyncio kütüphanesiyle birleştirerek verimli, engellemesiz uygulamalar geliştirin.
-
Eşzamanlılık: Generator'ların işbirlikçi çoklu görev ve hafif eşzamanlılığı nasıl uygulayabildiğini öğrenin.
Öğrenmeye devam edin ve uzman olun. Python Developer kariyer yolumuzu ya da Python Programming beceri yolumuzu bugün alın. Başlamak için aşağıdaki bağlantıya tıklayın.

Yapay zekâ, makine öğrenimi ve veri bilimi alanlarında uzmanlaşmış teknik yazar; karmaşık fikirleri anlaşılır ve erişilebilir kılar.
Python Generator'ları Hakkında SSS
Python generator tam olarak nedir?
Python generator'ı, bir iterator döndürmek için yield anahtar sözcüğünü kullanan, değerleri birer birer üreten ve tüm diziyi bir defada saklamayarak bellek tasarrufu sağlayan özel bir fonksiyondur.
Generator'ları, liste kavramaları yerine ne zaman kullanmalıyım?
Generator'lar, bellek verimliliğinin önemli olduğu büyük veya sonsuz dizileri işlerken idealdir; liste kavramaları ise tekrarlı erişim veya rastgele indeksleme için tam bir listeye ihtiyaç duyduğunuzda uygundur.
Generator'lar performansı nasıl artırır?
Generator'lar, değerleri birer birer (tembel değerlendirme) üreterek değerleri anlık olarak hesaplar; bu da belleği azaltır ve verileri bellekte tam yapılar oluşturmaya kıyasla işlemleri hızlandırır.
Bir generator üzerinde birden fazla kez yineleme yapabilir miyim?
Hayır, generator'lar tam bir yinelemeden sonra tükenir. Yeniden yinelemek için yeni bir generator örneği oluşturmanız gerekir.
Generator'ların bazı pratik kullanım alanları nelerdir?
Generator'lar, büyük veri kümelerini işleme, gerçek zamanlı verileri akıtma, verimli boru hatları kurma ve algoritmik ile simülasyon görevlerindeki sonsuz dizileri yönetme gibi durumlarda kullanışlıdır.

Bir veri bilimi projesi üzerinde çalıştığınızı ve görevinizin, belleğe yüklendiğinde makinenizi çökerten kadar büyük bir veri kümesini işlemek olduğunu hayal edin. Ya da canlı bir veri akışı gibi sonsuz bir diziyle uğraşıyorsunuz ve her şeyi aynı anda saklamanız mümkün değil. Bunlar, veri bilimcilerin kahveye—ve bazen de reset tuşuna—uzanmasına neden olan türden zorluklardır.
Bu yazıda Python generator'larını ve bunları kodunuzu basitleştirmek için nasıl kullanabileceğinizi öğreneceğiz. Bu fikir biraz pratik gerektirir; Python'a yeniyseniz ve yazıda biraz kaybolursanız, sağlam bir temel oluşturmak için Introduction to Python kursumuzu deneyin.