---
title: 'Yineleyici Protokolü'
source: 'https://academia.sh/tr/kurslar/python-veri-yapilari/yineleyici-protokolu'
course: 'Veri Yapıları ve Fonksiyonel Araçlar'
language: tr
updated: '2026-08-17T18:10:26+00:00'
license: 'CC BY-SA 4.0'
---

# Yineleyici Protokolü

Öğeleri görmek için kabı yeniden kurmak gerekmez: dilim kopyası üç başvuruyu yeni bir listeye yazarken yineleyici sıfır yazar, üçünü de paylaşır ve hiçbir öğe var etmez.

Önceki ders kopyanın bir katman derinliğinde kaldığını ölçtü: dilimle alınan liste yeni bir
kaptı, ama içindeki her öğe eskisiyle aynı nesneydi. Yine de o kabı kurmak bedava değildi.
Yeni liste öğeleri çoğaltmıyordu, fakat her birinin başvurusunu kendi içine yazmak
zorundaydı — kopya öğeleri üretmiyor, hepsine **dokunuyordu**.

Bu dersin sorusu bir adım öteye gider. Bir koleksiyonun öğelerini baştan sona görmek için o
kabı yeniden kurmak gerekli mi? Öğelere hiç dokunmadan, hiçbir yeni kap kurmadan
üzerlerinden geçmek mümkün mü? Python'un yanıtı **yineleyici (iterator)** sözleşmesidir ve
bu ders o sözleşmeyi kursun ekseninden ölçer: geçiş kaç nesne var ediyor, kaçını tutuyor,
kaçını paylaşıyor?

## Sözleşme

Sözleşme iki yerleşik ve iki özel yöntemden ibarettir. `iter(n)` bir nesneden yineleyici
ister ve nesnenin `__iter__` yöntemini çağırır. `next(y)` yineleyiciden sıradaki öğeyi
ister ve `__next__` çağırır. Verecek öğe kalmadığında `__next__` bir değer döndürmez,
`StopIteration` fırlatır.

Python Temelleri kursunun döngüler dersi bu protokolü **kullanan** taraftan ölçmüştü: `for`
yazımının hangi özel yöntemi kaç kez çağırdığı ve döngüyü bitiren şeyin bir dönüş değeri
değil bir istisna olduğu orada kuruldu; o ölçüm burada tekrarlanmaz. Bu dersin tarafı
öbürüdür — sözleşmeyi **yazan** taraf. Aynı protokole uyan iki nesne dışarıdan tamamen aynı
görünür, ama biri öğeleri önceden var edip tutar, öbürü istendikçe var edip hiç tutmaz.
Sözdizim bu farkı söylemez; sayı söyler.

## Yinelenebilir ile Yineleyici

Sözleşme iki rol tanımlar ve karıştırılmaları en sık görülen kusurdur. **Yinelenebilir**
bir nesne `__iter__` tanımlar; kendisi gezilmez, gezecek olanı verir. **Yineleyici**
`__next__` tanımlar ve asıl gezme işini o yapar. Liste bir yinelenebilirdir ama yineleyici
değildir: `next(liste)` çalışmaz, önce `iter(liste)` çağrılması gerekir.

Bir nesnenin ikisini birden tanımlaması yasak değildir. O zaman `__iter__` gövdesinde
kendisini döndürür ve ayrım kaybolur; sonucu aşağıda ölçülüyor. Ayrımın ayakta kaldığı
durumda `__iter__` her çağrıldığında **taze bir yineleyici** üretir.

Buradan çıkan tanım kursun eksenine oturur: yineleyicinin taşıdığı şey öğeler değil,
**nerede kalındığıdır**. Bir konum bir öğe değildir ve öğe sayısıyla büyümez.

Ölçümün varsayımları:

- **YN1** — Kâhin kurgunun kendisidir: `Oge` her üretimini sayar ve paylaşım `is` ile
  gösterilir; "kaç nesne var edildi" sorusunun ölçüsü bu sayaçtır.
- **YN2** — Ortak tanımın `Oge` sınıfı sayma düzeniyle birlikte aynen kullanılır.
- **YN3** — Kaynak üç öğelidir. Sayı küçük tutulmuştur, çünkü ölçülen şey büyüklük değil,
  geçişin kap kurup kurmadığıdır.
- **YN4** — "Kapta tutulan", geçişten çıkan nesnenin kendi içine yazdığı öğe başvurusu
  sayısıdır: bir liste için uzunluğu, bir yineleyici için sıfır. **Ölçü sayılan
  başvurudur, ölçülen bayt değil.**
- **YN5** — "Paylaşılan", geziden çıkan öğelerden kaçının kaynaktaki nesneyle `is`
  bakımından aynı olduğudur.
- **YN6** — `Sayim` öğeleri istendikçe var eder ve hiçbirini saklamaz; `Kap` öğeleri
  kuruluşta var edip bir listede tutar. İki sınıf yalnız bu noktada ayrılır ve ikisi de
  aynı sözleşmeyi imzalar.
- **YN7** — Tek geçişlilik ölçümü aynı nesne üzerinde `list()` iki kez çağrılarak yapılır;
  ikinci çağrının döndürdüğü öğe sayısı ölçüdür.
- **YN8** — Nöbetçi ölçümünde `next` ikinci argümanla çağrılır ve tükenmiş yineleyici
  üzerinde iki kez denenir; amacı tükenmenin kalıcı olduğunu göstermektir.
- **YN9** — İki geçiş ölçümünde üç yolun üçü de aynı beş öğelik kaynağı ister; değişen tek
  şey öğelerin nereden geldiğidir. Var edilen sayısı iki geçişin toplamıdır, tutulan ise
  geçişler arasında canlı kalan başvuru sayısıdır.

## Ölçüm

```python
"""Yineleyici sozlesmesi: gecis kac nesne var eder, kac basvuru tutar."""

SAYAC = {"uretilen": 0}


class Oge:
    """Her uretimi sayan oge."""

    def __init__(self, deger):
        SAYAC["uretilen"] += 1
        self.deger = deger

    def __repr__(self):
        return f"Oge({self.deger})"


def sifirla():
    SAYAC["uretilen"] = 0


def uretilen():
    return SAYAC["uretilen"]


def tutulan(nesne):
    """Nesnenin canli tuttugu Oge sayisi: kendisinde ya da bir liste ozniteliginde."""
    if isinstance(nesne, list):
        return sum(isinstance(o, Oge) for o in nesne)
    sayi = 0
    for d in getattr(nesne, "__dict__", {}).values():
        if isinstance(d, list):
            sayi += sum(isinstance(o, Oge) for o in d)
    return sayi


KAYNAK = [Oge(i) for i in range(3)]
BICIMLER = (("iter(kaynak)", lambda: iter(KAYNAK)),
            ("kaynak[:]", lambda: KAYNAK[:]),
            ("list(kaynak)", lambda: list(KAYNAK)),
            ("reversed(kaynak)", lambda: reversed(KAYNAK)),
            ("kavrama ile kopya", lambda: [Oge(o.deger) for o in KAYNAK]))

print(f"{'geçiş biçimi':<20s} {'yeni kap':>9s} {'var edilen':>11s}"
      f" {'kapta tutulan':>14s} {'paylaşılan':>11s}")
for ad, kur in BICIMLER:
    sifirla()
    nesne = kur()
    kap = "liste" if isinstance(nesne, list) else "yok"
    tut = tutulan(nesne)
    ortak = sum(1 for o in list(nesne) if any(o is k for k in KAYNAK))
    print(f"  {ad:<18s} {kap:>9s} {uretilen():11d} {tut:14d} {ortak:11d}")

y = iter(KAYNAK)
print()
print(f"iter(kaynak) is kaynak -> {y is KAYNAK}, iter(y) is y -> {iter(y) is y}")
print(f"next(y) is kaynak[0] -> {next(y) is KAYNAK[0]}")
a, b = iter(KAYNAK), iter(KAYNAK)
print(f"ayrı iki yineleyici: {len(list(a))} ve {len(list(b))} öğe")
t = iter(KAYNAK)
print(f"aynı yineleyici iki kez: {len(list(t))} ve {len(list(t))} öğe")


class Sayim:
    """Ogeleri istendikce var eden yineleyici."""

    def __init__(self, n):
        self.n, self.i = n, 0

    def __iter__(self):
        return self

    def __next__(self):
        if self.i >= self.n:
            raise StopIteration
        self.i += 1
        return Oge(self.i - 1)


class Kap:
    """Ogeleri onceden var edip tutan yinelenebilir."""

    def __init__(self, n):
        self.ogeler = [Oge(i) for i in range(n)]

    def __iter__(self):
        return iter(self.ogeler)


print()
print(f"{'nesne':<10s} {'kurulumda':>9s} {'kurulumda tutulan':>18s}"
      f" {'iki öğe sonra':>14s} {'__iter__ verdiği':>17s}")
for ad, kur in (("Sayim(5)", lambda: Sayim(5)), ("Kap(5)", lambda: Kap(5))):
    sifirla()
    n = kur()
    kurulum, kurulum_tut = uretilen(), tutulan(n)
    g = iter(n)
    next(g), next(g)
    print(f"  {ad:<8s} {kurulum:9d} {kurulum_tut:18d} {uretilen():14d}"
          f" {('kendisi' if iter(n) is n else 'başka nesne'):>17s}")

s = Sayim(3)
print()
print(f"Sayim(3) birinci geçiş {len(list(s))} öğe, ikinci geçiş {len(list(s))} öğe")
print(f"tükendikten sonra: {next(s, 'nöbetçi')!r} ve {next(s, 'nöbetçi')!r}")
k = Kap(3)
print(f"Kap(3) birinci geçiş {len(list(k))} öğe, ikinci geçiş {len(list(k))} öğe")

kalanlar = [3, 2, 1, 0, 9]
print(f"iter(çağrılabilir, nöbetçi) -> {list(iter(lambda: kalanlar.pop(0), 0))}")


def dogrudan():
    s = Sayim(5)
    return len(list(s)), len(list(s)), 0


def listeye_al():
    s = list(Sayim(5))
    return len(list(s)), len(list(s)), len(s)


def yeniden_kur():
    return len(list(Sayim(5))), len(list(Sayim(5))), 0


print()
print(f"{'iki geçiş yolu':<28s} {'var edilen':>11s} {'tutulan':>8s}"
      f" {'birinci':>8s} {'ikinci':>7s}")
for ad, f in (("aynı yineleyiciyi gezmek", dogrudan),
              ("listeye alıp gezmek", listeye_al),
              ("her geçişte yeniden kurmak", yeniden_kur)):
    sifirla()
    bir, iki, tut = f()
    print(f"  {ad:<26s} {uretilen():11d} {tut:8d} {bir:8d} {iki:7d}")
```

```
geçiş biçimi          yeni kap  var edilen  kapta tutulan  paylaşılan
  iter(kaynak)             yok           0              0           3
  kaynak[:]              liste           0              3           3
  list(kaynak)           liste           0              3           3
  reversed(kaynak)         yok           0              0           3
  kavrama ile kopya      liste           3              3           0

iter(kaynak) is kaynak -> False, iter(y) is y -> True
next(y) is kaynak[0] -> True
ayrı iki yineleyici: 3 ve 3 öğe
aynı yineleyici iki kez: 3 ve 0 öğe

nesne      kurulumda  kurulumda tutulan  iki öğe sonra  __iter__ verdiği
  Sayim(5)         0                  0              2           kendisi
  Kap(5)           5                  5              5       başka nesne

Sayim(3) birinci geçiş 3 öğe, ikinci geçiş 0 öğe
tükendikten sonra: 'nöbetçi' ve 'nöbetçi'
Kap(3) birinci geçiş 3 öğe, ikinci geçiş 3 öğe
iter(çağrılabilir, nöbetçi) -> [3, 2, 1]

iki geçiş yolu                var edilen  tutulan  birinci  ikinci
  aynı yineleyiciyi gezmek             5        0        5       0
  listeye alıp gezmek                  5        5        5       5
  her geçişte yeniden kurmak          10        0        5       5
```

## Kap Kurmayan Geçiş

Üst tablo beş geçiş biçimini yan yana koyuyor ve onları üç sınıfa ayırıyor.

**Kabı yeniden kuranlar.** `kaynak[:]` ve `list(kaynak)` yeni bir liste üretiyor ve o
listeye **3** başvuru yazıyor. Var edilen `Oge` sayısı **0** — hiçbir öğe çoğaltılmıyor,
paylaşılan **3**. Bu, önceki dersin ölçtüğü yüzeysel kopyadır: dış kap yeni, içindekiler
eski.

**Öğeleri yeniden üretenler.** Son satırdaki kavrama **3** yeni `Oge` var ediyor ve
paylaşılan **0**'a düşüyor. Kap da yeni, içindekiler de yeni.

**Hiçbirini yapmayanlar.** `iter(kaynak)` ve `reversed(kaynak)` satırlarında yeni kap
**yok**, kapta tutulan **0**, var edilen **0**, paylaşılan yine **3**. Geçiş üç öğenin
üçünü de teslim ediyor, hiçbirini üretmiyor ve hiçbirinin başvurusunu kendi içine yazmıyor.
Girişteki soru buradan yanıtlanıyor: öğelere dokunmadan üzerlerinden geçmek mümkün.

Bir itiraz burada haklıdır: `iter(kaynak)` de bir nesne üretir — yineleyicinin kendisi.
Ama o nesne bir **kap değildir**; içinde öğe tutmaz, yalnız kaynağa bir başvuru ve bir
konum taşır. Tablodaki "var edilen" sütunu `Oge` sayar, "kapta tutulan" sütunu ise öğe
başvurusu; yineleyici ikisinde de sıfır veriyor çünkü ikisinden de değil.

Fark üç öğede bir başvuru meselesi gibi görünür. Kaynak bin öğeli olsaydı `list(kaynak)`
bin başvuru yazacak, `iter(kaynak)` yine sıfır yazacaktı. Kabın maliyeti öğe sayısıyla
büyür, yineleyicininki büyümez: **tutulan sayı öğe sayısının bir fonksiyonudur, konum
değildir.**

## Yineleyici Ne Taşır

Orta bloktaki üç satır rolleri ayırıyor. `iter(kaynak) is kaynak` **yanlış**: liste bir
yinelenebilirdir, ama gezme işini kendisi yapmaz; `iter` ondan ayrı bir nesne döndürür.
`iter(y) is y` ise **doğru**: bir yineleyiciden yineleyici istendiğinde kendisi gelir. Bu,
`for` döngüsünün hem koleksiyonları hem yineleyicileri aynı yazımla kabul edebilmesinin
nedenidir — ikisine de `iter` uygulanır ve ikincisinde bu çağrı bir şey değiştirmez.

`next(y) is kaynak[0]` **doğru**: teslim edilen öğe kaynağın öğesinin kendisidir, kopyası
değil. Yineleyici bir üretici değil, bir **teslimatçıdır**.

Sonraki iki satır konumun nerede tutulduğunu gösteriyor. Aynı listeden alınan **ayrı iki
yineleyici** birbirinden bağımsız: her biri **3** öğe veriyor. **Aynı yineleyici** iki kez
gezildiğinde **3** ve **0**. Konum listede değil yineleyicide durduğu için, listeden
istenen her yeni yineleyici sıfırdan başlar; var olan bir yineleyici ise kaldığı yerden
devam eder ve sonunda tükenir.

## Sözleşmeyi Yazan İki Sınıf

İkinci tablo aynı sözleşmeyi imzalayan iki sınıfı karşılaştırıyor. `Sayim` kurulumda **0**
nesne var ediyor ve **0** tutuyor; iki öğe istendiğinde üretilen **2**'ye çıkıyor. `Kap`
kurulumda **5** var ediyor, **5** tutuyor ve iki öğe istendikten sonra sayı yine **5** —
üretim çoktan bitmişti.

İki nesne dışarıdan ayırt edilemez. İkisi de `for` döngüsüne girer, ikisi de aynı beş
değeri verir, ikisinde de `iter` ve `next` çalışır. Ayrım yalnız sayıdadır: biri işi
**kuruluşta**, öbürü **istendikçe** yapar.

Son sütun ikinci ayrımı veriyor. `Sayim.__iter__` **kendisini** döndürüyor; `Kap.__iter__`
her çağrıda iç listesinden **başka bir nesne** alıyor. Bunun sonucu alttaki iki satırda:
`Sayim(3)` birinci geçişte **3**, ikinci geçişte **0** öğe veriyor — tükeniyor. `Kap(3)`
her iki geçişte de **3** veriyor, çünkü her geçiş taze bir yineleyiciyle başlıyor.

Tek geçişlilik bir kusur değil, sözleşmenin doğrudan sonucudur. Sözleşme "istendiğinde
sıradaki öğeyi ver" der; "aynı öğeleri ikinci kez de ver" demez. Bir yineleyicinin ikinci
geçişi verebilmesi için gördüğü her öğeyi saklaması gerekirdi — yani tam olarak kaçındığı
şeyi yapması. **Tutmayan nesne geri saramaz.**

Tükenme kalıcıdır. Boşalmış `Sayim` üzerinde `next` iki kez daha çağrıldığında ikisinde de
nöbetçi değer geliyor; nesne kendini yenilemiyor.

## İki Geçişin Bedeli

Son tablo bu sonucun pratikteki karşılığını üç yolla ölçüyor. Elde tek geçişlik bir kaynak
var ve öğeler iki kez lazım.

**Aynı yineleyiciyi iki kez gezmek** işe yaramıyor: **5** ve **0**. Var edilen **5**,
tutulan **0** — en ucuz satır, ama ikinci geçişi vermiyor.

**Listeye alıp iki kez gezmek** ikinci geçişi veriyor: **5** ve **5**. Var edilen yine
**5**, çünkü öğeler bir kez üretilip saklandı. Bedel tutulan sütununda: **0**'dan **5**'e
çıktı. Öğe sayısı bin olsaydı bu sütun bin olacaktı.

**Her geçişte kaynağı yeniden kurmak** da ikinci geçişi veriyor ve tutulan **0**'da
kalıyor. Bedeli öbür sütunda: var edilen **5**'ten **10**'a çıktı, çünkü beş öğe iki kez
üretildi. Bu yol ancak kaynak yeniden kurulabiliyorsa geçerlidir; bir ağ akışı ya da bir
kullanıcı girdisi ikinci kez aynı öğeleri vermeyebilir.

Üç satırın hiçbiri ötekilerin toplamını yenmiyor ve ortada bir seçim var: **tutmadan iki
geçiş olmaz.** Ya öğeleri saklarsınız, ya yeniden üretirsiniz, ya da tek geçişe razı
olursunuz. Yineleyici bu seçimi ortadan kaldırmaz; yalnız üçüncü seçeneği ücretsiz kılar.

## Sözleşmenin İki Kaçış Kapısı

Son iki satır sözleşmenin yerleşik düzeyindeki iki esnekliğini gösteriyor.

`next(y, nöbetçi)` ikinci argümanla çağrıldığında `StopIteration` fırlatmaz, verilen değeri
döndürür. İstisna bir değere çevrilmiş olur; `try` bloğu kurmadan "bitti mi?" sorusunu
sormanın yoludur. Nöbetçi olarak seçilen değerin akışta **geçemeyecek** bir değer olması
gerekir, yoksa bitiş ile öğe birbirine karışır — bu, `StopIteration`'ın neden bir dönüş
değeri olmadığının aynı gerekçesidir.

`iter` iki argümanla çağrıldığında bambaşka bir şey yapar: kaynağı bir koleksiyon değil,
bir **çağrı** olan yineleyici üretir. Örnekte her `next` bir listeden baştan bir öğe alıyor
ve nöbetçi olan **0** göründüğünde geçiş bitiyor. Sonuç `[3, 2, 1]` — nöbetçinin kendisi
teslim edilmiyor. Böylece dosya, kuyruk ya da her çağrıda bir sonrakini veren herhangi bir
kaynak, koleksiyona hiç dönüştürülmeden `for` döngüsüne sokulabilir.

## Özet

- Yineleyici sözleşmesi iki yerleşik ve iki özel yöntemdir: `iter` `__iter__`'i, `next`
  `__next__`'i çağırır; öğe kalmadığında `StopIteration` fırlatılır.
- Geçmek, kopyalamak ve yeniden üretmek üç ayrı iştir: `iter` yeni kap kurmaz ve **0**
  başvuru yazar, `list(kaynak)` **3** yazar, kavrama ayrıca **3** yeni nesne var eder.
  Üçünde de paylaşım ölçülür ve yalnız sonuncusunda **0**'dır.
- Yinelenebilir `__iter__` tanımlar, yineleyici `__next__` tanımlar; `iter(liste)` listeden
  ayrı bir nesne verir, `iter(yineleyici)` nesnenin kendisini verir. Yineleyicinin taşıdığı
  şey öğeler değil, kalınan konumdur.
- Aynı sözleşmeyi imzalayan iki sınıf dışarıdan ayırt edilemez ama sayıları ayrıdır:
  kurulumda **0**'a karşı **5** üretim, **0**'a karşı **5** tutma.
- Tek geçişlilik sözleşmenin sonucudur: `__iter__` kendisini döndüren nesne **3** ve **0**
  öğe verir, taze yineleyici döndüren nesne iki geçişte de **3**. Tutmayan nesne geri
  saramaz ve tükenme kalıcıdır.

## Sonraki Adım

`Sayim` sınıfı beş satırda üç yöntem, bir sınır ve bir konum alanı istedi; hepsi tek bir
şey için — nerede kalındığını hatırlamak. Bu, elle yazılan her yineleyicide tekrarlanan bir
iştir ve konum tek bir sayı olmadığında (iç içe bir gezinti, dallanmış bir arama) sınıf
hızla büyür. Sonraki ders, dilin bu işi yazana bırakmayan biçimini ölçüyor: gövdesi
duraklayıp kaldığı yerden devam eden bir işlev. Ölçülecek sayı şudur — böyle bir işlevi
çağırmak, gövdesindeki bin üretimden kaçını **çağrı anında** yapar?
