Python'da Generator'lar

Python'da Generator'lar

Bu makale, Python'daki generator'ları açıklar.

YouTube Video

Python'da Generator'lar

Genel Bakış

Python'daki generator'lar, bir iterator türüdür ve tekrarlayan işlemleri verimli bir şekilde yürütmek için güçlü bir özelliktir. Büyük miktarda veriyi işlerken bellek dostu kod yazmanızı sağlarlar.

Generator Nedir?

Python'daki bir generator, yield anahtar kelimesiyle tanımlanan, her seferinde bir değer üreten özel bir fonksiyondur. Özelliği, çalışmayı mevcut durumunu koruyarak duraklatması ve daha sonra devam ettirebilmesidir.

yield Temelleri

yield, bir değer döndüren ve aynı anda fonksiyonun yürütülmesini duraklatan bir anahtar kelimedir.

 1def simple_generator():
 2    yield 1
 3    yield 2
 4    yield 3
 5
 6gen = simple_generator()
 7
 8print(next(gen))  # 1
 9print(next(gen))  # 2
10print(next(gen))  # 3
  • Çağrıldığında, bu fonksiyon her seferinde bir değer döndüren bir generator nesnesi verir.
  • next()'i bir sonraki değer olmadığında çağırırsanız, bir StopIteration hatası oluşur.

next() ve StopIteration

 1def simple_generator():
 2    yield 1
 3    yield 2
 4    yield 3
 5
 6gen = simple_generator()
 7
 8try:
 9    while True:
10        value = next(gen)
11        print(value)
12except StopIteration:
13    print("Finished")
  • Bu şekilde StopIteration hatasını açıkça ele alarak, bir jeneratörün ne zaman sona erdiğini tespit edebilirsiniz.

send(value)

send(value) çağrısı, jeneratörü devam ettirir ve value değerini yield ifadesinin bulunduğu konuma gönderir. Gönderilen değer, jeneratör tarafında yield ifadesinin dönüş değeri olarak alınabilir. İlk çağrıda, send(value) ile None dışında bir şey gönderemezsiniz, bu nedenle next() veya send(None) kullanmalısınız.

 1def gen():
 2    x = yield 1
 3    print(f"x = {x}")
 4    y = yield 2
 5    print(f"y = {y}")
 6
 7g = gen()
 8print(next(g))       # -> 1 (value from yield 1)
 9print(g.send(10))    # -> x = 10, 2 (value from yield 2)
10print(g.send(20))    # -> y = 20, StopIteration occurs
  • send(10) ile, jeneratörün yield ifadesi 10 döndüren bir ifadeye dönüşür ve 10 değeri x'e atanır.

throw()

throw çağrısı, jeneratörü devam ettirir ve beklemede olan yield konumunda bir istisna (exception) oluşturur. İstisnayı jeneratör içinde yakalayarak işlemi sürdürmeye devam edebilirsiniz. İstisna yakalanmazsa, dışarıya yayılır ve jeneratör sona erer.

 1def gen():
 2    try:
 3        yield 1
 4    except ValueError as e:
 5        print(f"Caught: {e}")
 6        yield "recovered"
 7
 8g = gen()
 9print(next(g))   # -> 1
10print(g.throw(ValueError("boom")))  # -> Caught: boom, "recovered"
  • Bu kodda, jeneratöre bir istisna enjekte etmek için throw çağrılır. Jeneratör tarafında, istisna yakalanır ve recovered değeri döndürülür.

close()

close() çağrısı jeneratörü sonlandırır. Jeneratör içinde, finally bloğunu kullanarak temizlik işlemleri yapabilirsiniz. close() çağrıldıktan sonra next() veya send() çağrısı yapmak StopIteration hatasına neden olur.

1def gen():
2    try:
3        yield 1
4    finally:
5        print("Cleaning up...")
6
7g = gen()
8print(next(g))  # -> 1
9g.close()       # -> Cleaning up...
  • Bu kod, close() çağrısının jeneratörü sonlandırdığını ve finally bloğundaki temizlik işlemini tetiklediğini gösterir.

yield from

yield from, bir alt üreteci yetkilendirmek için kullanılan bir sözdizimidir. Bir üretecin içinde başka bir üreteci çağırmanın ve tüm değerlerini dış kapsama aktarmanın basit bir yoludur.

1def sub_gen():
2    yield 1
3    yield 2
4
5def main_gen():
6    yield from sub_gen()
7    yield 3
8
9print(list(main_gen()))  # -> [1, 2, 3]
  • Bu kod, alt üreteçteki tüm değerleri yield from kullanarak dış üretece aktarır ve ardından 3 değerini üretir.

Iterator'larla İlişki

Generator'lar, dahili olarak __iter__() ve __next__() metodlarını uygulayarak bir iterator türü olurlar. Bu nedenle, for döngüsü gibi iterable işlemlerle tamamen uyumludurlar.

for Döngüleriyle Entegrasyonu

Python'da, bir for döngüsü, değerleri otomatik olarak almak için dahili olarak next() kullanır.

1def simple_generator():
2    yield 1
3    yield 2
4    yield 3
5
6for value in simple_generator():
7    print(value)

Bu yöntemle, StopIteration yönetimi de otomatik olarak gerçekleşir.

Sonsuz Generator'lar Oluşturmak

1def count_up(start=0):
2    while True:
3        yield start
4        start += 1
5
6counter = count_up()
7print(next(counter))  # 0
8print(next(counter))  # 1

Sonsuz döngüler oluşturmak mümkündür ancak kullanırken dikkatli olunmalıdır.

Generator İfadeleri

Parantez kullanılarak yazılan generator ifadeleri, liste kavramalarına benzer bir sözdizimiyle generator tanımlamanıza olanak sağlar.

1# List comprehension (generates the entire list at once)
2squares_list = [x**2 for x in range(5)]
3print(squares_list)
4
5# Generator expression
6squares_gen = (x**2 for x in range(5))
7for square in squares_gen:
8    print(square)

Liste kavramalarından farklı olarak, tüm elemanları birden belleğe yüklemezler, bu da onları daha bellek verimli hale getirir.

Generator'larda Hata Yönetimi

Bir generator içerisinde istisnalar oluşabilir. Bu gibi durumlarda, normal Python kodunda olduğu gibi try-except kullanırsınız.

 1def safe_divide_generator(numbers, divisor):
 2    """Yields results of dividing numbers by a given divisor safely."""
 3    for number in numbers:
 4        try:
 5            yield number / divisor  # Attempt to divide and yield result.
 6        except ZeroDivisionError:
 7            yield float('inf')  # Return infinity if division by zero occurs.
 8
 9# Example usage
10numbers = [10, 20, 30]
11gen = safe_divide_generator(numbers, 0)  # Create generator with divisor as 0.
12for value in gen:
13    print(value)  # Output: inf, inf, inf

Bu örnekte, sıfıra bölme durumunda uygun bir hata yönetimi gerçekleştirilir.

Bir Generator'ın Yığını (Stack Trace)

Jeneratörün içinde bir istisna oluşursa, jeneratör devam ettirildiğinde bu istisna ortaya çıkar.

 1def error_generator():
 2    """A generator that yields values and raises an error."""
 3    yield 1
 4    raise ValueError("An error occurred")  # Raise a ValueError intentionally.
 5    yield 2
 6
 7gen = error_generator()
 8print(next(gen))       # Output: 1 (first value yielded)
 9try:
10    print(next(gen))   # Attempt to get the next value, which raises an error
11except ValueError as e:
12    print(e)           # Output: An error occurred (exception message is printed)
  • Bu jeneratör önce 1 değerini döndürür. Devam ettirildiğinde ortaya çıkan hata yakalanır ve bir hata mesajı olarak gösterilir.

Generator Kullanımına Örnekler

Bir dosyayı satır satır okumak (büyük dosyalar için uygundur)

1def read_large_file(filepath):
2    with open(filepath, 'r') as f:
3        for line in f:
4            yield line.strip()
  • Bu fonksiyon bir metin dosyasını satır satır bir yineleyici kullanarak okur, her satırdaki boşlukları temizler ve bunu bir jeneratör olarak döndürerek büyük dosyaların düşük bellek kullanımıyla işlenmesini sağlar.

Fibonacci Dizisi için Generator

1def fibonacci(limit):
2    a, b = 0, 1
3    while a < limit:
4        yield a
5        a, b = b, a + b
6
7for n in fibonacci(100):
8    print(n)
  • Bu kod, bir jeneratör aracılığıyla üst sınırdan küçük Fibonacci sayılarını sırasıyla üretir ve bunları bir for döngüsü kullanarak ekrana yazdırır.

Kullanım Durumları

Jeneratörler ayrıca aşağıdaki durumlarda da kullanılabilir.

  • Büyük CSV veya günlük dosyalarının sıralı işlenmesi
  • API sayfalandırması
  • Akış verilerini işleme (ör. Kafka, IoT cihazları)

Özet

Kavram Ana Nokta
yield Duraklatır ve bir değer döndürür
Generator Fonksiyonu yield içeren ve çağrıldığında bir iterator döndüren fonksiyon
Avantajları Bellek dostu ve büyük veri kümelerini işlemek için idealdir
Generator İfadesi (x for x in iterable) gibi özlü bir sözdizimi sağlar

Generator'ları kullanarak, belleği koruyarak ve kodunuzu özlü tutarak büyük veri kümelerini verimli bir şekilde işleyebilirsiniz.

Yukarıdaki makaleyi, YouTube kanalımızda Visual Studio Code'u kullanarak takip edebilirsiniz. Lütfen YouTube kanalını da kontrol edin.

YouTube Video