---
title: 'Veri Yapıları ve Fonksiyonel Araçlar'
source: 'https://academia.sh/tr/kurslar/python-veri-yapilari'
language: tr
updated: '2026-08-17T04:42:16+00:00'
license: 'CC BY-SA 4.0'
lessons: 16
outcomes:
    - 'Yerleşik koleksiyonları maliyet ve anlam farkıyla seçer'
    - 'Üreteç ve yineleyicilerle bellek dostu akışlar kurar'
    - 'Dekoratörlerle davranışı yeniden kullanılabilir biçimde sarmalar'
---

# Veri Yapıları ve Fonksiyonel Araçlar

Aynı sonucu veren iki biçim aynı sayıda nesne üretebilir ama farklı sayıda tutar; kurs koleksiyonları, tembel akışları ve modül sistemini var edilen nesne, paylaşılan nesne ve yapılan karşılaştırma üzerinden ölçer.

## Koleksiyonlar

Değişebilir sıralı koleksiyonun maliyetleri, değişmezliğin getirdiği güvence, anahtar-değer eşlemesi ve sıralama davranışı, küme işlemleri, özelleşmiş kaplar ve dilimlemenin ne kopyalayıp ne paylaştığı.

### Listeler

Source: https://academia.sh/tr/kurslar/python-veri-yapilari/listeler

Liste öğeleri değil onlara giden bağları tutar; bin öğe koymak 1000 nesne var eder ama listeyi ikinci bir ada bağlamak 0, aynı öğeyi beş kez eklemek 1 var eder ve sekiz büyütme biçiminden yalnız üçü yeni kap kurar.

Python Temelleri kursu on altı derste tek bir iddiayı ölçtü: sözdizim bir kısaltmadır ve her
biçim adı belli bir özel yöntemi çağırır. Kapanışında bıraktığı ayrım şuydu: **protokolü
kullanmakla protokolü yazmak ayrı şeylerdir.** Orada ölçülen nesne kurgu bir nesneydi;
hiçbir yerleşik koleksiyondan inmiyordu ve `for` içine girebilmesinin tek gerekçesi ilgili
yöntemleri tanımlamış olmasıydı.

Bu kursta aynı protokolleri **dilin kendi kapları** taşır. Onlar da aynı yöntemleri
tanımlar, ama artık bir gösteri için değil, gerçek veri için. Ve gerçek veriyle birlikte
soru da değişir: bir kabın **ne yapabildiği** değil, **neye mal olduğu**. Bu dersin sorusu
budur — bir liste kurulduğunda ortaya kaç nesne çıkar, bunların kaçı listenin kendi ürünüdür
ve liste onların kaçını canlı tutar?

#### Kap Neyi Tutar

Bir **liste (list)** öğelerini içine **almaz**. Sıralı bir yuva dizisi tutar ve her yuva
bir nesneye giden bağdır. Bu ayrım Python'a özgü değil, nesne modelinin doğrudan sonucudur:
bir ada değer bağlamak nesneyi kopyalamaz, ona bir ad daha verir. Programlama Temelleri
kursunda atamanın bir **bağlama** olduğu, `is` ile `==` ayrımı ve takma ad kavramı zaten
kuruldu ve burada tekrarlanmaz. Yeni olan şey, aynı bağlamanın **bir ada değil bir yuvaya**
yapılabilmesidir.

Sonucu şudur: listeyi büyüten her işlem yuva sayısını artırır, ama nesne sayısını
artırmayabilir. Aynı nesne beş yuvada durabilir; o zaman liste beş uzunluktadır ve içinde
tek bir nesne vardır.

Sıralı kapların kendi yapı kuramı — dizi, dinamik dizi, bağlı liste, büyüme oranı — Veri
Yapıları kursunda kuruldu ve burada tekrarlanmaz. Orada **düzenek** kuruldu; burada
Python'un o düzeneği hangi kapla verdiği ve bunun **kaç nesneye** mal olduğu ölçülüyor.

#### Ortak Kurgu: Her Üretimini Sayan Öge

Ölçüm için tek bir sınıf yeter. `Oge`, her üretiminde ortak bir sayacı bir artırır; başka
hiçbir şey yapmaz. Bir işlemin kaç nesne var ettiğini bilmek için sayacı sıfırlamak, işlemi
yapmak ve sayacı okumak yeter. Kâhin kurgunun kendisidir: üretimi nesne kendi saydığı için,
paylaşımı `is` gösterdiği için biliriz. Kurs boyunca üç sayı ayrı ayrı yazılır — **var
edilen** nesne, **tutulan** yuva ve bunların kaçının **ayrı** nesneye gittiği.

Nesnelerin kimliği hiçbir yerde sayı olarak basılmaz. İki yuvanın aynı nesneye gidip
gitmediği yalnız `is` ile sınanır; ayrı nesne sayısı da bu sınamaların toplamıdır.

- **KL1** — Sayaç yalnız `Oge` üretimini sayar; listenin, demetin ya da başka bir kabın
  kendisi sayılmaz. Kap sayısı ayrıca, kimlik sınamasıyla okunur.
- **KL2** — Her satırda sayaç işlemden **hemen önce** sıfırlanır; ölçülen şey yalnız o
  satırın var ettiğidir.
- **KL3** — Ayrı nesne sayısı `is` karşılaştırmalarıyla bulunur; hiçbir kimlik değeri
  basılmaz ve sayıya bakılarak kimlik hakkında başka bir çıkarım yapılmaz.
- **KL4** — Bin öğelik ölçüm listenin büyüme davranışını değil, yalnız var edilen nesne
  sayısını sınar; büyüme sınıfı Algoritmalar kursunun konusudur ve burada ölçülmez.
- **KL5** — Çarpma ölçümünde çarpılan liste **tek öğeliktir**; böylece çarpımın yuva
  çoğaltmakla nesne çoğaltmak arasındaki farkı tek başına görünür.

```python
"""Liste bir kaptir: yuvalari tutar, ogeleri kendisi var etmez."""

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 ayri(kap):
    """Kimlikce ayri nesne sayisi. Kimlik `is` ile sinanir, sayisi basilmaz."""
    benzersiz = []
    for x in kap:
        if not any(x is y for y in benzersiz):
            benzersiz.append(x)
    return len(benzersiz)


N = 1000

print(f"{'işlem':<32s}{'var edilen':>11s}{'yuva':>6s}{'ayrı nesne':>12s}")

sifirla()
liste = []
for i in range(N):
    liste.append(Oge(i))
print(f"{'bin öğeyi listeye koymak':<32s}{uretilen():>11d}{len(liste):>6d}"
      f"{ayri(liste):>12d}")

sifirla()
takma = liste
print(f"{'listeyi ikinci bir ada bağlamak':<32s}{uretilen():>11d}"
      f"{len(takma):>6d}{ayri(takma):>12d}")

sifirla()
tek = Oge(0)
elle = [tek, tek, tek, tek, tek]
print(f"{'aynı öğeyi beş kez eklemek':<32s}{uretilen():>11d}{len(elle):>6d}"
      f"{ayri(elle):>12d}")

sifirla()
carpim = [tek] * 5
print(f"{'tek öğelik listeyi beşle çarpmak':<32s}{uretilen():>11d}"
      f"{len(carpim):>6d}{ayri(carpim):>12d}")

print()
print(f"takma is liste -> {takma is liste} | "
      f"carpim[0] is carpim[4] -> {carpim[0] is carpim[4]} | "
      f"carpim[0] is tek -> {carpim[0] is tek}")
print(f"liste[0] is liste[1] -> {liste[0] is liste[1]}")
```

```
işlem                            var edilen  yuva  ayrı nesne
bin öğeyi listeye koymak               1000  1000        1000
listeyi ikinci bir ada bağlamak           0  1000        1000
aynı öğeyi beş kez eklemek                1     5           1
tek öğelik listeyi beşle çarpmak          0     5           1

takma is liste -> True | carpim[0] is carpim[4] -> True | carpim[0] is tek -> True
liste[0] is liste[1] -> False
```

#### Dört Satırın Söylediği

Birinci satır beklendiği gibi: bin öğe koymak **1000** nesne var eder, **1000** yuva tutar
ve bunların hepsi ayrı nesnedir. Ama bu 1000 nesneyi var eden şey **liste değildir**. Onları
`Oge(i)` çağrısı üretti; liste yalnız yuvaları tuttu. Aynı bin nesne hiç listeye konmadan da
var edilebilirdi ve sayaç yine 1000 gösterirdi.

İkinci satır bunun kanıtıdır: aynı listeyi ikinci bir ada bağlamak **0** nesne var eder.
Yuva sayısı değişmez, ayrı nesne sayısı değişmez, sadece nesneye giden ad sayısı bir artar —
ve `takma is liste` bunu doğrular. Bin öğelik bir listeyi bir işleve geçirmek de aynı şeydir
ve maliyeti aynıdır: **sıfır**.

Üçüncü satır ayrımı tersinden gösteriyor. Beş yuva var, ama var edilen nesne **1** ve ayrı
nesne **1**. Liste beş uzunlukta, içinde tek bir nesne. Dördüncü satır aynı sonucu
sözdizimin kendisiyle üretiyor: `[tek] * 5` hiçbir şey var etmiyor — **0** — ve beş yuvanın
beşi de aynı nesneye gidiyor. Çarpma yuva çoğaltır, nesne çoğaltmaz.

Son iki satır kimlik sınamalarını veriyor. `carpim[0] is carpim[4]` **True**: çarpımın
ürettiği yuvalar aynı nesneyi paylaşıyor. `liste[0] is liste[1]` ise **False**: bin öğelik
listede yuvaların hepsi ayrı nesneye gidiyor, çünkü her biri ayrı bir `Oge(i)` çağrısından
geldi. İki liste de beş ya da bin uzunluktadır; farkı uzunluk değil, yuvaların **nereye**
baktığı belirler.

#### Büyütmenin Biçimleri

Listeyi büyütmenin birden çok yazımı vardır ve hepsi aynı uzunluğu vermez, aynı kabı
vermez. Python Temelleri kursunda `n + m` ile `n += m` ayrımı **hangi yöntemi çağırdıklarıyla**
ölçülmüştü: ilki `__add__`, ikincisi `__iadd__`. Burada ölçülen şey aynı ayrımın **kaç kap**
var ettiğidir. İkinci ölçüm sekiz yazımı yan yana koyar ve her birine üç soru sorar: kaç
yeni öğe var edildi, sonuç **aynı kap mı** kaldı ve yuvaların kaçı **eskiden var olan** bir
nesneye gidiyor.

- **KL6** — Sekiz biçim de aynı beş öğelik havuzun **tazeden kurulmuş** bir listesiyle
  başlar; havuzun öğeleri ölçümden önce var edilir ve sayaç her biçimden önce sıfırlanır.
- **KL7** — "Aynı kap" sütunu, işlemden önce tutulan bağla sonucun `is` karşılaştırmasıdır;
  kap sayısı basılmaz, yalnız aynılık sınanır.
- **KL8** — "Eski nesneye giden yuva" sütunu, sonuçtaki her yuvanın havuzdaki ya da ek
  listedeki bir nesneyle `is` karşılaştırılmasıyla bulunur.
- **KL9** — Sıralama ölçümü bir **anahtar işleviyle** yapılır; öğe sınıfına karşılaştırma
  yöntemi eklenmez, böylece sayma düzeni değişmez. Sıralamanın kaç karşılaştırma yaptığı bu
  derste ölçülmez.

```python
"""Listeyi buyutmenin bicimleri: hangisi yeni kap kurar, hangisi var olani buyutur."""

SAYAC = {"uretilen": 0}


class 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 deger(o):
    return o.deger


HAVUZ = [Oge(i) for i in (3, 1, 4, 1, 5)]
EK = [Oge(i) for i in (9, 2, 6)]
ESKILER = HAVUZ + EK


def sona_ekle(a):
    a.append(Oge(0))
    return a


def basa_ekle(a):
    a.insert(0, Oge(0))
    return a


def uzat(a):
    a.extend(EK)
    return a


def toplama(a):
    return a + EK


def yerinde_toplama(a):
    a += EK
    return a


def carpma(a):
    return a * 2


def siralama(a):
    return sorted(a, key=deger)


def yerinde_siralama(a):
    a.sort(key=deger)
    return a


BICIMLER = (("a.append(yeni)", sona_ekle), ("a.insert(0, yeni)", basa_ekle),
            ("a.extend(b)", uzat), ("a = a + b", toplama),
            ("a += b", yerinde_toplama), ("a = a * 2", carpma),
            ("a = sorted(a, ...)", siralama), ("a.sort(...)", yerinde_siralama))

print(f"{'biçim':<20s}{'yeni öğe':>9s}{'aynı kap':>10s}{'yuva':>6s}"
      f"{'eski nesneye giden yuva':>25s}")
for ad, islem in BICIMLER:
    a = list(HAVUZ)
    once = a
    sifirla()
    sonuc = islem(a)
    eski = sum(1 for x in sonuc if any(x is y for y in ESKILER))
    print(f"{ad:<20s}{uretilen():>9d}{str(sonuc is once):>10s}"
          f"{len(sonuc):>6d}{eski:>25d}")
```

```
biçim                yeni öğe  aynı kap  yuva  eski nesneye giden yuva
a.append(yeni)              1      True     6                        5
a.insert(0, yeni)           1      True     6                        5
a.extend(b)                 0      True     8                        8
a = a + b                   0     False     8                        8
a += b                      0      True     8                        8
a = a * 2                   0     False    10                       10
a = sorted(a, ...)          0     False     5                        5
a.sort(...)                 0      True     5                        5
```

#### Aynı Uzunluk, Ayrı Bedel

"Yeni öğe" sütunu sekiz satırın altısında **0**. Yalnız `append` ve `insert` birer nesne var
ediyor, o da eklenmek üzere yazılan `Oge(0)` çağrısından. Geri kalan altı biçim tek bir öğe
bile üretmiyor: hepsi var olan nesnelere giden **yeni yuvalar** açıyor ya da mevcut yuvaları
yeniden düzenliyor. Son sütun bunu satır satır doğruluyor — yuvaların tamamı, eklenen taze
öğe dışında, ölçümden önce var edilmiş nesnelere gidiyor.

Asıl ayrım "aynı kap" sütununda. `a.extend(b)` ile `a = a + b` aynı sekiz öğeli sonucu
veriyor, ama biri var olan kabı büyütüyor, öbürü **yeni bir kap** kurup adı ona bağlıyor.
`a += b` ise adına rağmen toplama değil, uzatma yapıyor: sonuç **aynı kaptır**. Python
Temelleri kursunda bu ikisinin ayrı özel yöntem çağırdığı sayılmıştı; buradaki sayı o
çağrının sonucudur. Farkın gözlenebilir olması için listeye başka bir ad bağlı olması yeter:
`a += b` yazıldığında o ad da uzamış listeyi görür, `a = a + b` yazıldığında eski beş öğeli
listede kalır.

`sorted` ile `sort` aynı ikiliği sıralama tarafında tekrarlıyor. İkisi de **0** yeni öğe
üretiyor ve ikisinin de sonucunda **5** yuva var; fark, birinin yeni bir kap döndürüp öbürünün
var olan kabı yerinde yeniden dizmesi. `sort` bir liste döndürmez, `None` döndürür — çünkü
döndürecek yeni bir kap yoktur.

Bir şey bu tablonun **dışındadır** ve tabloya bakarak bulunamaz: `append` ile `insert(0, ...)`
aynı sayıları veriyor, oysa yaptıkları iş aynı değildir. Başa eklemek kendinden sonraki bütün
yuvaları kaydırmayı gerektirir, sona eklemek gerektirmez. Bu fark nesne sayısında görünmez,
çünkü kaydırma nesne var etmez — yalnız yuvaları yeniden yazar. Kaydırmanın büyüme sınıfı
Algoritmalar kursunda kuruldu ve burada tekrarlanmaz; bu dersin ölçtüğü eksende iki biçim
gerçekten eşittir.

#### Silmek Neyi Serbest Bırakır

Ekleme tarafı bir kabın nesne var etmediğini gösterdi. Silme tarafı bunun karşılığını
gösterir: bir kap nesne **yok da etmez**. Silme işlemleri yalnız yuvayı kapatır; nesnenin
yaşamaya devam edip etmediğini ona bağlı kalan başka adlar ve başka yuvalar belirler.
Üçüncü ölçüm bir listeyi dört ayrı yazımla boşaltır ve her adımda aynı öğeye bağlı ikinci
bir kaba bakar.

- **KL10** — Ölçüm tek bir liste üzerinde **sırayla** ilerler; her satır bir öncekinin
  bıraktığı durumdan devam eder ve yuva sayıları bu yüzden azalarak gider.
- **KL11** — İkinci kap ölçüm başında kurulur ve ölçüm boyunca **hiç dokunulmaz**; ondaki
  değişiklik yalnız birinci kaptan gelebilirdi.
- **KL12** — Eşitliğe bakan silme, öğe sınıfı eşitlik tanımlamadığı için **kimliğe düşer**;
  bu ders eşitlik tanımlayan bir öğeyle ölçüm yapmaz.

```python
"""Silmek nesneyi yok etmez, bagi koparir. Paylasilan oge oteki kapta durur."""

SAYAC = {"uretilen": 0}


class 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"]


sifirla()
a = [Oge(i) for i in range(5)]
elde = a[2]
c = [a[0], a[2]]
kurulum = uretilen()

print(f"{'işlem':<20s}{'var edilen':>11s}{'a yuva':>8s}{'c yuva':>8s}"
      f"{'c[1] is elde':>14s}")
print(f"{'beş öğeyle kurmak':<20s}{kurulum:>11d}{len(a):>8d}{len(c):>8d}"
      f"{str(c[1] is elde):>14s}")


def sondan_al():
    a.pop()


def bastan_sil():
    del a[0]


def esitle_sil():
    a.remove(elde)


def bosalt():
    a.clear()


for ad, islem in (("a.pop()", sondan_al), ("del a[0]", bastan_sil),
                  ("a.remove(elde)", esitle_sil), ("a.clear()", bosalt)):
    sifirla()
    islem()
    print(f"{ad:<20s}{uretilen():>11d}{len(a):>8d}{len(c):>8d}"
          f"{str(c[1] is elde):>14s}")

print()
print(f"a boşaldıktan sonra elde tutulan öğe: {elde} | c: {c}")
```

```
işlem                var edilen  a yuva  c yuva  c[1] is elde
beş öğeyle kurmak             5       5       2          True
a.pop()                       0       4       2          True
del a[0]                      0       3       2          True
a.remove(elde)                0       2       2          True
a.clear()                     0       0       2          True

a boşaldıktan sonra elde tutulan öğe: Oge(2) | c: [Oge(0), Oge(2)]
```

Dört silme yazımı da **0** nesne var ediyor — bu beklenirdi. Söylediği asıl şey sağdaki iki
sütunda. Birinci kabın yuvası beşten sıfıra inerken ikinci kabın yuvası **2**'de sabit
kalıyor ve `c[1] is elde` her satırda **True**. Birinci liste tamamen boşaldıktan sonra bile
paylaşılan öğe okunabiliyor: son satır `Oge(2)` basıyor.

Buradan çıkan kural şudur: **bir kaptan silmek, o kabın o nesneye giden bağını koparır;
nesneyi değil.** Nesne, ona bağlı kalan başka bir ad ya da başka bir yuva olduğu sürece
durur. Bin öğelik bir listeyi boşaltmak, o bin nesnenin serbest kalacağı anlamına gelmez —
öğelerin bir kısmı başka bir kapta duruyorsa orada durmayı sürdürür. Bir kabın "tuttuğu"
nesne bu yüzden ölçülen üç sayıdan biridir: var edilen nesne sayısı işi kimin yaptığını,
tutulan yuva sayısı kabın büyüklüğünü, paylaşım ise silmenin gerçekte neyi serbest
bıraktığını söyler.

Eşitliğe bakan silme bir ayrıntı ekliyor. `remove` bir yuvayı kimliğine göre değil,
**eşitliğine** göre bulur; ölçümdeki öğe sınıfı eşitlik tanımlamadığı için eşitlik burada
kimliğe düşüyor ve iki ölçüt aynı sonucu veriyor. Eşitlik tanımlayan bir öğede ikisi
ayrışırdı: `remove` eşit görünen **ilk** yuvayı silerdi, elde tutulan nesneyi değil.

#### Özet

- Liste öğeleri içine almaz; her yuva bir nesneye giden bağdır. Bin öğe koymak **1000** nesne
  var eder, listeyi ikinci bir ada bağlamak **0**.
- Yuva sayısı ile nesne sayısı ayrı sayılardır: aynı öğeyi beş kez eklemek **5** yuva ve
  **1** ayrı nesne verir, `[tek] * 5` ise **0** nesne var edip beş yuvayı aynı nesneye
  bağlar.
- Büyütmenin sekiz biçiminden yalnız `append` ve `insert` yeni öğe var eder; kalan altısı
  yalnız yuva açar ve yuvaların hepsi eskiden var olan nesnelere gider.
- `a = a + b`, `a = a * 2` ve `sorted` yeni bir kap kurar; `extend`, `a += b` ve `sort` var
  olan kabı yerinde değiştirir — aynı uzunluk, ayrı kap.
- Nesne sayısı her farkı göstermez: `append` ile `insert(0, ...)` bu eksende eşittir, çünkü
  yuva kaydırmak nesne var etmez.

#### Sonraki Adım

Bu dersin bütün ölçümleri listenin **değiştirilebilir** olmasına dayanıyordu: yuva
eklenebiliyor, silinebiliyor, yeniden dizilebiliyordu. Peki bir kap bunların hiçbirine izin
vermezse ne kazanır? Sonraki ders aynı ölçümleri değişmez bir sıralı kapta yineler ve iki
soruyu sorar: değişmezlik **hangi işlemi** gerçekten imkânsız kılar ve bu imkânsızlığın
karşılığında kabın kazandığı yetki nedir? Yanıtın bir ucu, listenin yapamadığı bir işte
görünecek — bir kabın başka bir kabın **anahtarı** olması.

### Demetler

Source: https://academia.sh/tr/kurslar/python-veri-yapilari/demetler

Altı işlemden üçü demette istisnayla düşer ama iç öğeyi değiştiren işlem demette de geçer; değişmezlik bir katman derinliğindedir ve karşılığında kabın kazandığı yetki, beş adayın üçünde görüldüğü gibi anahtar olabilmektir.

Önceki dersin bütün ölçümleri listenin değiştirilebilir olmasına dayanıyordu: yuva
eklenebiliyor, silinebiliyor, yeniden dizilebiliyordu. Var edilen nesne sayısı çoğu biçimde
sıfırdı, çünkü liste yalnız yuvaları düzenliyordu.

Bu ders aynı yuvaları **değişmez** bir kapta yeniler. **Demet (tuple)** de sıralı bir
yuva dizisidir, ama kurulduktan sonra yuvaları yeniden yazılamaz. Soru iki parçalıdır ve
ikisi de sayılabilir: değişmezlik **hangi işlemi** gerçekten imkânsız kılıyor ve bu
imkânsızlığın karşılığında kap **hangi yetkiyi** kazanıyor?

#### Değişmezlik Neyi Kapatır

Değişmezlik kavramının kendisi bu kursun konusu değildir. Programlama Temelleri kursunda
değiştirilebilir ile değiştirilemez nesnenin ayrımı kuruldu; Yazılım Tasarımı ve Mimari
İlkeler kursu değişmezliği paylaşılan durumu ortadan kaldıran bir **tasarım tekniği** olarak
ele aldı. İkisi de burada tekrarlanmaz. Burada ölçülen şey daha dar ve daha somut: aynı
işlem dizisi iki kapta çalıştırıldığında **hangi satırlar geçiyor, hangileri istisnayla
düşüyor.**

Ayrımın nerede durduğunu baştan söylemek gerekir, çünkü ölçüm tam oraya bakacak. Bir demet
**kendi yuvalarını** korur. Yuvaların gösterdiği nesnelerin içine karışmaz ve karışamaz —
onlar ayrı nesnelerdir ve kendi kurallarına tabidir. Demetin verdiği güvence bu yüzden
"içindekiler değişmez" değil, **"hangi nesneye baktığım değişmez"** güvencesidir.

- **KL13** — Altı işlem her iki kapta da **aynı sırayla** ve her seferinde tazeden kurulmuş
  bir kapla denenir; kaplar aynı değerlerden üretilir.
- **KL14** — Sonuç sütunu üç değerden birini alır: işlem geçtiyse sonucun **aynı kap** mı
  yoksa **yeni kap** mı olduğu, geçmediyse düşen istisnanın **sınıf adı**.
- **KL15** — "Aynı kap" kararı işlemden önce tutulan bağla `is` karşılaştırmasıdır.
- **KL16** — Sıralama bir **anahtar işleviyle** yapılır; öğe sınıfına karşılaştırma yöntemi
  eklenmez ve sayma düzeni değişmez.
- **KL17** — İç öğeyi değiştiren satır kabın kendisine hiç dokunmaz; yalnız bir yuvanın
  gösterdiği nesnenin alanına yazar.

```python
"""Ayni alti islem listede ve demette: degismezlik neyi imkansiz kiliyor."""

SAYAC = {"uretilen": 0}


class 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 deger(o):
    return o.deger


HAVUZ = [Oge(i) for i in (3, 1, 4)]


def yerine_yaz(k):
    k[0] = Oge(9)
    return k


def sona_ekle(k):
    k.append(Oge(9))
    return k


def sil(k):
    del k[0]
    return k


def yerinde_topla(k):
    k += type(k)((Oge(9),))
    return k


def ic_ogeyi_degistir(k):
    k[0].deger = 9
    return k


def sirala(k):
    return sorted(k, key=deger)


ISLEMLER = (("k[0] = yeni", yerine_yaz), ("k.append(yeni)", sona_ekle),
            ("del k[0]", sil), ("k += (yeni,)", yerinde_topla),
            ("k[0].deger = 9", ic_ogeyi_degistir), ("sorted(k, ...)", sirala))

print(f"{'işlem':<18s}{'listede':<16s}{'demette'}")
for ad, islem in ISLEMLER:
    satir = []
    for tip in (list, tuple):
        k = tip(Oge(o.deger) for o in HAVUZ)
        once = k
        try:
            sonuc = islem(k)
            satir.append("aynı kap" if sonuc is once else "yeni kap")
        except Exception as e:
            satir.append(type(e).__name__)
    print(f"{ad:<18s}{satir[0]:<16s}{satir[1]}")

print()
demet = tuple(HAVUZ)
sifirla()
buyuk = demet + (Oge(9),)
print(f"demete ekleme: var edilen {uretilen()} | eski yuva {len(demet)} -> "
      f"yeni yuva {len(buyuk)}")
print(f"buyuk is demet -> {buyuk is demet} | "
      f"buyuk[0] is demet[0] -> {buyuk[0] is demet[0]}")

print()
print(f"demet[0].deger okuma: {demet[0].deger}")
demet[0].deger = 9
print(f"iç öğe değiştirildikten sonra: {demet[0].deger} | "
      f"demet[0] is HAVUZ[0] -> {demet[0] is HAVUZ[0]}")
```

```
işlem             listede         demette
k[0] = yeni       aynı kap        TypeError
k.append(yeni)    aynı kap        AttributeError
del k[0]          aynı kap        TypeError
k += (yeni,)      aynı kap        yeni kap
k[0].deger = 9    aynı kap        aynı kap
sorted(k, ...)    yeni kap        yeni kap

demete ekleme: var edilen 1 | eski yuva 3 -> yeni yuva 4
buyuk is demet -> False | buyuk[0] is demet[0] -> True

demet[0].deger okuma: 3
iç öğe değiştirildikten sonra: 9 | demet[0] is HAVUZ[0] -> True
```

#### Üç Satır Düşüyor, Üç Satır Geçiyor

Altı işlemin üçü demette istisnayla düşüyor ve üçü geçiyor. Düşen üçü aynı şeyi denedi:
**var olan bir yuvayı yeniden yazmak, yuva eklemek ya da yuva silmek.** Düşme biçimleri
ayrı. Yuvaya yazmak ve yuva silmek `TypeError` veriyor — bu iki yazım demette de bir
anlama gelir, ama demet o anlamı desteklemiyor. Sona ekleme ise `AttributeError` veriyor:
demette `append` diye bir yöntem **yoktur**. Birincisi "yapamam", ikincisi "böyle bir şey
bilmiyorum" der.

Geçen üç satırın ikisi zaten kabı değiştirmiyordu. `sorted` her iki kapta da **yeni kap**
veriyor; zaten hep yeni bir liste döndürür ve kaynağa dokunmaz. `k += (yeni,)` satırı ise
ayrımın tam ortasında duruyor: listede **aynı kap**, demette **yeni kap**. Yazım aynı, sonuç
aynı görünüyor, ama biri var olan kabı uzatıyor, öbürü yeni bir kap kurup adı ona bağlıyor.
Demet değişmez olduğu için yerinde büyütme demette bir uzatma değil, bir **yeniden
kurmadır**.

Ortadaki sayılar bu yeniden kurmanın faturasını veriyor: var edilen **1** ve o bir tane de
eklenen öğenin kendisi. Kap büyüyor — üç yuvadan dört yuvaya — ama kabın içindeki eski üç
nesne yeniden üretilmiyor. `buyuk is demet` **False**, yani kap gerçekten yeni; ama
`buyuk[0] is demet[0]` **True**, yani öğe eski. Bir demeti bin kez bir öğe ekleyerek
büyütmek, bin kap kurmak demektir; içindeki nesneleri bin kez kopyalamak demek değildir.

Son iki satır bu dersin en kritik ölçümüdür. `k[0].deger = 9` demette **geçiyor** — hem de
hiçbir uyarı vermeden. Demet kendi yuvasını korudu: yuva hâlâ aynı nesneye bakıyor ve
`demet[0] is HAVUZ[0]` bunu doğruluyor. Ama o nesnenin **içi** değişti; okunan değer **3**
iken **9** oldu. Demek ki **değişmezlik bir katman derinliğindedir**: kabın verdiği güvence
yuvaların hangi nesneyi gösterdiğiyle sınırlıdır, o nesnelerin durumuyla değil.

#### Değişmezliğin Karşılığı

Kapatılan üç işlemin karşılığında bir yetki açılır. Bir nesnenin **karmalanabilir** olması,
ondan sabit bir **karma değeri** hesaplanabilmesi demektir; bu değer nesnenin ömrü boyunca
değişmemelidir. Yuvaları yeniden yazılabilen bir kapta böyle bir söz verilemez, çünkü kap
değiştiğinde karma değeri de değişirdi ve nesne konduğu yerde kaybolurdu. Demet bu sözü verebilir —
ama yalnız içindekiler de verebiliyorsa.

- **KL18** — Karmalanabilirlik doğrudan sınanır: karma değeri alma denenir ve düşen istisnanın
  sınıf adı yazılır. **Karma değeri basılmaz**; ölçülen şey değerin kendisi değil, alınıp
  alınamadığıdır.
- **KL19** — Anahtar olabilme ayrı bir sütundur ve tek girdilik bir eşleme kurularak
  sınanır; iki sütun birbirini doğrular.
- **KL20** — Çoklu dönüş ölçümünde işlev **var olan** öğeleri döndürür; böylece sayılan şey
  yalnız dönüşün ve çözmenin kendi maliyetidir.
- **KL21** — Çözmede var edilen sayısı, dönüşten sonraki sayaçtan dönüşteki sayı düşülerek
  bulunur; iki aşama ayrı ayrı okunur.

```python
"""Demetin kazandigi yetki: anahtar olabilmek ve coklu donus degeri tasimak."""

SAYAC = {"uretilen": 0}


class 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"]


ADAYLAR = (("(2031, 7)", (2031, 7)), ("[2031, 7]", [2031, 7]),
           ("((2031, 7), (2032, 1))", ((2031, 7), (2032, 1))),
           ("(2031, [7])", (2031, [7])), ("'kuzey'", "kuzey"))

print(f"{'aday':<24s}{'karmalanabilir':<16s}{'anahtar olabilir'}")
for ad, x in ADAYLAR:
    try:
        hash(x)
        h = "evet"
    except TypeError as e:
        h = type(e).__name__
    try:
        {x: 0}
        a = "evet"
    except TypeError as e:
        a = type(e).__name__
    print(f"{ad:<24s}{h:<16s}{a}")

KAYIT = {(2031, 7): "yamac", (2031, 8): "kuzey", (2032, 1): "ozet"}
print()
print(f"demet anahtarlı sözlük: {len(KAYIT)} girdi | "
      f"KAYIT[(2031, 8)] -> {KAYIT[(2031, 8)]!r}")

HAVUZ = [Oge(i) for i in (3, 1, 4)]


def uclu():
    return HAVUZ[0], HAVUZ[1], HAVUZ[2]


sifirla()
sonuc = uclu()
donus_uretimi = uretilen()
a, b, c = sonuc
cozme_uretimi = uretilen()
print()
print(f"dönüş kabının tipi {type(sonuc).__name__} | yuva {len(sonuc)} | "
      f"dönüşte var edilen {donus_uretimi} | çözmede var edilen "
      f"{cozme_uretimi - donus_uretimi}")
print(f"a is HAVUZ[0] -> {a is HAVUZ[0]} | c is HAVUZ[2] -> {c is HAVUZ[2]}")

sifirla()
x, y = HAVUZ[0], HAVUZ[1]
x, y = y, x
print(f"iki adı takas etmek: var edilen {uretilen()} | "
      f"x is HAVUZ[1] -> {x is HAVUZ[1]}")

sifirla()
bas, *orta = HAVUZ
print(f"yıldızlı çözme: var edilen {uretilen()} | orta bir "
      f"{type(orta).__name__} ve {len(orta)} yuva | "
      f"orta[0] is HAVUZ[1] -> {orta[0] is HAVUZ[1]}")
```

```
aday                    karmalanabilir  anahtar olabilir
(2031, 7)               evet            evet
[2031, 7]               TypeError       TypeError
((2031, 7), (2032, 1))  evet            evet
(2031, [7])             TypeError       TypeError
'kuzey'                 evet            evet

demet anahtarlı sözlük: 3 girdi | KAYIT[(2031, 8)] -> 'kuzey'

dönüş kabının tipi tuple | yuva 3 | dönüşte var edilen 0 | çözmede var edilen 0
a is HAVUZ[0] -> True | c is HAVUZ[2] -> True
iki adı takas etmek: var edilen 0 | x is HAVUZ[1] -> True
yıldızlı çözme: var edilen 0 | orta bir list ve 2 yuva | orta[0] is HAVUZ[1] -> True
```
#### Beş Adayın Üçü Geçiyor

Tablo karmalanabilirliğin kaba değil, **kabın bütününe** baktığını gösteriyor. Sayı çifti
geçiyor, iç içe demet de geçiyor — çünkü içindeki her şey de karmalanabilir. Liste düşüyor;
bu beklenirdi. Asıl satır dördüncüsüdür: **bir liste taşıyan demet de düşüyor.** Kabın
kendisi değişmez olduğu hâlde. Bir demetin karma değeri içindekilerin karma
değerlerinden hesaplanır ve içlerinden biri karma değeri veremiyorsa demet de veremez.

Bu, bir önceki bölümün bulgusunun ikinci yüzüdür. Orada değişmezliğin bir katman derinliğinde
olduğunu gördük; burada dilin bunu **bildiği** görülüyor. Demet, güvenceyi ancak
içindekilerin de verdiği kadar verebilir ve veremediğinde anahtar olma yetkisini kaybeder.
İki sütunun aynı yanıtı vermesi rastlantı değildir: eşlemeye anahtar olma koşulu tam olarak
karma değeri alınabilmesidir.

Alt bloktaki üç ölçüm demetin görünmeyen kullanımını sayıyor. Bir işlev virgülle ayrılmış
üç değer döndürdüğünde ortaya bir **demet** çıkar — bu, ayrı bir sözdizim değil, demet
kurmanın parantezsiz yazımıdır. Dönüşte var edilen **0**, çözmede var edilen **0**. İşlev
üç nesneyi paketleyip verdi, atama üç adı o nesnelere bağladı; hiçbir aşamada kopya
çıkarılmadı ve `a is HAVUZ[0]` bunu doğruluyor.

İki adı takas eden satır aynı düzeneğin en kısa hâlidir: sağ taraf bir demet kurar, sol
taraf onu çözer, var edilen öğe **0**. Yıldızlı çözme ise bir ayrıntı ekliyor — artan
yuvaları toplayan kap bir demet değil, bir **listedir** ve o listenin yuvaları yine eski
nesnelere gidiyor. Çoklu dönüş değeri bu yüzden ucuz bir yazımdır: taşınan şey nesneler
değil, onlara giden bağlardır.

#### Anahtar Neye Göre Bulunur

Bir demetin anahtar olabilmesi ölçüldü; anahtarın **nasıl bulunduğu** ölçülmedi. Bu ayrım
önemlidir, çünkü önceki dersin silme ölçümünde eşitliğe bakan bir yöntem kimliğe düşmüştü.
Eşlemede öyle bir düşüş yoktur: anahtar her zaman karma değeri ve eşitlikle aranır, kimlikle değil.

- **KL22** — Aranan anahtar iki değişkenden **çalışma anında** kurulur; eşlemenin içindeki
  anahtar ise kaynak metindeki yazımdan gelir. İkisinin ayrı nesne olduğu `is` ile sınanır.
- **KL23** — Karma değerlerinin **eşitliği** karşılaştırılır; değerlerin kendisi basılmaz.
- **KL24** — Aynı değerleri taşıyan üç demet bir döngüde ayrı ayrı kurulur; ikisi eşittir ve
  eşlemede kaç girdi kaldığı sayılır.

```python
"""Anahtar kimlikle degil esitlikle bulunur; kabin tipi esitlige girer."""

KAYIT = {(2031, 7): "yamac", (2031, 8): "kuzey"}
YIL, AY = 2031, 7
ARANAN = (YIL, AY)   # calisma aninda kurulur, sabit degildir
ILK = next(iter(KAYIT))

print(f"aranan is sözlükteki anahtar -> {ARANAN is ILK}")
print(f"aranan == sözlükteki anahtar -> {ARANAN == ILK}")
print(f"karma değerleri eşit -> {hash(ARANAN) == hash(ILK)}")
print(f"KAYIT[aranan] -> {KAYIT[ARANAN]!r}")

print()
print(f"(2031, 7) == [2031, 7] -> {(2031, 7) == [2031, 7]}")
print(f"(2031, 7) == (2031, 7, None) -> {(2031, 7) == (2031, 7, None)}")
for aday in ((2031, 7), [2031, 7]):
    try:
        var = aday in KAYIT
    except TypeError as e:
        var = type(e).__name__
    print(f"{str(aday):<12s} in KAYIT -> {var}")

print()
DEMETLER = [(2031, a) for a in (7, 8, 7)]
print(f"üç demet kuruldu | ayrı nesne mi: "
      f"{DEMETLER[0] is DEMETLER[2]} | eşit mi: {DEMETLER[0] == DEMETLER[2]}")
KUME = {d: i for i, d in enumerate(DEMETLER)}
print(f"üçü de anahtar yapıldı, eşlemede kalan girdi: {len(KUME)}")
```

```
aranan is sözlükteki anahtar -> False
aranan == sözlükteki anahtar -> True
karma değerleri eşit -> True
KAYIT[aranan] -> 'yamac'

(2031, 7) == [2031, 7] -> False
(2031, 7) == (2031, 7, None) -> False
(2031, 7)    in KAYIT -> True
[2031, 7]    in KAYIT -> TypeError

üç demet kuruldu | ayrı nesne mi: False | eşit mi: True
üçü de anahtar yapıldı, eşlemede kalan girdi: 2
```

İlk dört satır aranan kuralı veriyor: nesne **ayrı**, değer **eşit**, özetler **eşit** ve
arama başarılı. Anahtarı bulan şey kimlik değildir. Bu, demetin ucuz bir anahtar olmasının
nedenidir — anahtarı taşımak gerekmez, aynı değerlerden yenisini kurmak yeter.

Aradaki satırlar eşitliğin **kap tipini de kapsadığını** gösteriyor. Aynı iki sayıyı taşıyan
liste demede eşit değildir ve tipi kaldırıp yalnız içeriğe bakan bir kural yoktur. Uzunluğu
farklı bir demet de eşit değildir. Üyelik sınamasında ayrım daha da serttir: liste yalnız
eşit sayılmakla kalmaz, `TypeError` ile düşer — çünkü aranmadan önce karma değeri alınması gerekir
ve o adım hiç gerçekleşmez.

Son iki satır eşitliğin sonucunu somutlaştırıyor. Üç demet ayrı ayrı kuruldu; birinci ile
üçüncü ayrı nesne ama eşit. Üçü de anahtar yapıldığında eşlemede **2** girdi kalıyor: eşit
iki anahtar aynı yere düştü ve ikincisi birincinin değerini ezdi. Kabın kimliği hiçbir yerde
işe karışmadı.

#### Özet

- Değişmezlik üç işlemi kapatır: yuvaya yazmak, yuva silmek ve yuva eklemek. İlk ikisi
  `TypeError`, sonuncusu `AttributeError` ile düşer — biri "yapamam", öbürü "bilmiyorum" der.
- Demette `k += (yeni,)` bir uzatma değil **yeniden kurmadır**: kap yenidir
  (`buyuk is demet` → False) ama öğeler eskidir (`buyuk[0] is demet[0]` → True) ve var
  edilen **1**'dir.
- Değişmezlik **bir katman derinliğindedir**: `k[0].deger = 9` demette de geçer, çünkü demet
  yuvasını korur, yuvanın gösterdiği nesnenin içini değil.
- Kabın kazandığı yetki anahtar olabilmektir ve bu yetki **içindekilere bağlıdır**: beş
  adaydan üçü geçer, liste taşıyan demet kabın kendisi değişmez olduğu hâlde düşer.
- Çoklu dönüş değeri bir demettir ve **0** öğe var eder; çözme de **0** var eder, çünkü
  taşınan şey nesneler değil onlara giden bağlardır.

#### Sonraki Adım

Demet anahtar **olabildi**; ama anahtarı alıp değere ulaştıran kabın kendisi henüz
ölçülmedi. Sonraki ders eşlemeye bakar ve kursun dördüncü okumasının ilk yarısını öder:
aynı iki yüz öğe bir listede ve bir kümede arandığında kaç karşılaştırma yapılır? Veri aynı,
öğeler aynı, değişen tek şey onları hangi kapta tuttuğumuz. Aynı ders bir ikinci soruyu daha
sorar: bir sözlüğün anahtarlarını istediğimizde elimize geçen şey bir kopya mı, yoksa
sözlüğe bakmayı sürdüren bir pencere mi?

### Sözlükler

Source: https://academia.sh/tr/kurslar/python-veri-yapilari/sozlukler

Aynı iki yüz öğe listede aranınca 200, kümede ve sözlükte 1 karşılaştırma yapar; anahtarlar istendiğinde eline geçen şey kopya değil görünümdür ve sonradan eklenen anahtarı görür — 2'den 3'e çıkar, anlık liste kopyası 2'de kalır.

Önceki ders bir demetin anahtar **olabildiğini** gösterdi ve anahtarın kimlikle değil
eşitlikle bulunduğunu ölçtü. Anahtarı alıp değere ulaştıran kabın kendisi ise ölçülmedi.

**Sözlük (dict)**, anahtarı değere bağlayan kaptır. Bu ders ona iki soru sorar ve ikisinin
de yanıtı sayıdır. Birincisi kursun dördüncü okumasının ilk yarısıdır: aynı iki yüz öğe bir
listede ve bir kümede arandığında **kaç karşılaştırma** yapılır? Veri aynı, öğeler aynı;
değişen tek şey onları hangi kapta tuttuğumuz. İkincisi kopyalamayla ilgilidir: bir sözlüğün
anahtarlarını istediğimizde elimize geçen şey bir **kopya** mı, yoksa sözlüğe bakmayı
sürdüren bir **pencere** mi?

#### Eşleme Neye Bakar

Bir liste bir öğeyi ararken elinde tek bir yol vardır: yuvaları sırayla gezmek ve her birini
aranana eşit mi diye sormak. Sözlük ve küme başka bir yol kullanır — anahtarın **karma değerinden**
onun hangi bölmeye düştüğünü hesaplar ve doğrudan oraya bakar. Bu yüzden ikisinin de anahtarı
karmalanabilir olmak zorundadır; önceki dersin ölçtüğü koşul buydu.

Bu düzeneğin kendisi bu kursun konusu değildir. Karma tablosunun nasıl kurulduğu, yük
çarpanı, çakışma çözümü ve tablonun büyüme davranışı **Veri Yapıları** kursunda kuruldu;
karmaşıklık çözümlemesi ve büyüme sınıfları **Algoritmalar** kursunda kuruldu. İkisi de
burada tekrarlanmaz. Orada **düzenek** kuruldu; burada Python'un o düzeneği hangi kapla
verdiği ve tek bir sınamanın **kaç somut karşılaştırmaya** mal olduğu ölçülüyor. Aşağıdaki
sayılar bir büyüme sınıfı iddiası değildir; belirli bir veriyle yapılmış bir sayımdır.

- **KL25** — Karşılaştırmayı öğenin kendisi sayar: eşitlik yöntemi her çağrıldığında ortak
  bir sayacı bir artırır. Kâhin kurgudur; sayı bir tahmin değil bir dökümdür.
- **KL26** — Üç kap **aynı iki yüz öğeden** kurulur; öğeler bir kez var edilir ve üç kap da
  aynı nesnelere bakar.
- **KL27** — Sayaç her sınamadan **hemen önce** sıfırlanır; kapların kurulması sırasında
  yapılan karşılaştırmalar ölçüme girmez.
- **KL28** — Aranan öğe her seferinde **yeniden kurulur**; böylece arama kimlikle değil
  eşitlikle yapılmak zorunda kalır.
- **KL29** — İki durum ayrı ayrı ölçülür: kapta **bulunan** bir öğe ve kapta **bulunmayan**
  bir öğe.
- **KL30** — Listedeki konum etkisi ayrıca ölçülür; ilk, ortadaki ve son öğe için sayı ayrı
  okunur.

```python
"""Ayni iki yuz oge, uc kap: uyelik sinamasi kac karsilastirma yapiyor."""


class Sayan:
    """Esitlik karsilastirmalarini sayan deger."""

    sayac = {"karsilastirma": 0}

    def __init__(self, d):
        self.d = d

    def __eq__(self, o):
        Sayan.sayac["karsilastirma"] += 1
        return isinstance(o, Sayan) and self.d == o.d

    def __hash__(self):
        return hash(self.d)


def olc(kap, aranan):
    Sayan.sayac["karsilastirma"] = 0
    bulundu = aranan in kap
    return Sayan.sayac["karsilastirma"], bulundu


N = 200
OGELER = [Sayan(i) for i in range(N)]
KAPLAR = (("liste", list(OGELER)), ("küme", set(OGELER)),
          ("sözlük", {o: o.d for o in OGELER}))

print(f"{'kap':<8s}{'son öğe aranıyor':>18s}{'bulundu':>9s}"
      f"{'olmayan öğe aranıyor':>22s}{'bulundu':>9s}")
for ad, kap in KAPLAR:
    var, b1 = olc(kap, Sayan(N - 1))
    yok, b2 = olc(kap, Sayan(N))
    print(f"{ad:<8s}{var:>18d}{str(b1):>9s}{yok:>22d}{str(b2):>9s}")

print()
ilk_var, _ = olc(KAPLAR[0][1], Sayan(0))
orta_var, _ = olc(KAPLAR[0][1], Sayan(N // 2))
print(f"listede ilk öğe {ilk_var} | ortadaki öğe {orta_var} | "
      f"son öğe {olc(KAPLAR[0][1], Sayan(N - 1))[0]}")
print(f"kümede ilk öğe {olc(KAPLAR[1][1], Sayan(0))[0]} | "
      f"ortadaki öğe {olc(KAPLAR[1][1], Sayan(N // 2))[0]} | "
      f"son öğe {olc(KAPLAR[1][1], Sayan(N - 1))[0]}")

print()
SOZLUK = KAPLAR[2][1]
Sayan.sayac["karsilastirma"] = 0
deger = SOZLUK[Sayan(N - 1)]
print(f"sözlükten değer okumak: {Sayan.sayac['karsilastirma']} karşılaştırma, "
      f"dönen değer {deger}")
```

```
kap       son öğe aranıyor  bulundu  olmayan öğe aranıyor  bulundu
liste                  200     True                   200    False
küme                     1     True                     0    False
sözlük                   1     True                     0    False

listede ilk öğe 1 | ortadaki öğe 101 | son öğe 200
kümede ilk öğe 1 | ortadaki öğe 1 | son öğe 1

sözlükten değer okumak: 1 karşılaştırma, dönen değer 199
```

#### İki Yüze Karşı Bir

Üst tablonun ilk sütunu kursun dördüncü okumasını veriyor: aynı öğeyi aramak listede **200**,
kümede ve sözlükte **1** karşılaştırma yapıyor. Veri aynı, öğeler aynı nesneler, aranan aynı
değer. Değişen tek şey erişim biçimi. **Maliyeti veri değil, erişim biçimi belirliyor.**

İkinci sütun ayrımı daha da keskinleştiriyor. Kapta **bulunmayan** bir öğe arandığında liste
yine **200** karşılaştırma yapıyor — bulamadığını anlamak için hepsine bakmak zorunda. Küme
ve sözlük ise **0** karşılaştırma yapıyor: karma hesabı onu boş bir bölmeye götürdü ve orada
karşılaştıracak bir şey bulamadı. Yokluğu kanıtlamak listede en pahalı iş, kümede ise en
ucuzudur.

Ortadaki iki satır listenin sayısının **tek bir sayı olmadığını** gösteriyor: ilk öğe **1**,
ortadaki **101**, son öğe **200** karşılaştırma. Listede maliyet aranan şeyin **nerede
durduğuna** bağlıdır. Kümede aynı üç arama **1**, **1**, **1** veriyor — konum ölçüme hiç
girmiyor, çünkü küme yuvaları gezmiyor, hesaplıyor.

Son satır sözlüğün asıl işini ölçüyor. Bir anahtardan değere ulaşmak **1** karşılaştırma
yapıyor; küme üyeliğiyle aynı sayı. Sözlük, kümenin yaptığı aramayı yapıp bulduğu yerde
değeri de tutar. İkisi aynı düzeneğin iki yüzüdür: küme yalnız "var mı" der, sözlük "var ve
karşılığı bu" der.

#### Görünüm mü, Anlık Kopya mı

Sözlüğün anahtarlarını, değerlerini ya da çiftlerini isteyen yazımlar bir liste döndürmez.
Döndürdükleri şey bir **görünümdür (view)**: sözlüğe bakmayı sürdüren, kendi yuvası olmayan
bir nesne. Bunun karşıtı **anlık kopyadır (snapshot)** — istendiği andaki durumu ayrı bir
kapta dondurup sözlükle bağını koparan liste. İkisi aynı anda kurulup sözlük değiştirildiğinde
fark ölçülebilir hâle gelir.

- **KL31** — Üç görünüm ve bir anlık kopya **aynı anda**, sözlüğe dokunulmadan önce kurulur;
  sonra tek bir anahtar eklenir ve dördü de yeniden okunur.
- **KL32** — Uzunluk her kaptan kendi yöntemiyle okunur; ölçüm bir kabın uzunluğunu başka bir
  kaptan çıkarmaz.
- **KL33** — Bin girdilik ölçümde anlık kopyanın yuva sayısı basılır; görünümün yuva sayısı
  **basılmaz**, çünkü görünüm yuva tutmaz.
- **KL34** — İki görünüm nesnesi hem `is` hem `==` ile karşılaştırılır; kimlik değeri
  basılmaz, yalnız aynılık sınanır.
- **KL35** — Dolaşma sırasında değişiklik iki kez denenir: bir kez görünüm üzerinde, bir kez
  anlık kopya üzerinde. İstisnayla biten deneme kesilmeden önce **bir anahtar eklemiştir** ve
  ikinci deneme o durumdan devam eder.
- **KL36** — Sıra ölçümünde üç anahtarlı küçük bir sözlük kullanılır; sıra, anahtar
  listesinin kendisinden okunur.

```python
"""Gorunum ile anlik kopya: hangisi sonradan eklenen anahtari goruyor."""

sozluk = {"a": 1, "b": 2}
anahtarlar = sozluk.keys()
degerler = sozluk.values()
ciftler = sozluk.items()
liste = list(sozluk.keys())

onceki = (len(anahtarlar), len(degerler), len(ciftler), len(liste))
sozluk["c"] = 3
sonraki = (len(anahtarlar), len(degerler), len(ciftler), len(liste))

print(f"{'kap':<22s}{'eklemeden önce':>15s}{'sonra':>7s}"
      f"{'yeni anahtarı görüyor':>23s}")
for ad, once, sonra, icerir in (
        ("keys() görünümü", onceki[0], sonraki[0], "c" in anahtarlar),
        ("values() görünümü", onceki[1], sonraki[1], 3 in degerler),
        ("items() görünümü", onceki[2], sonraki[2], ("c", 3) in ciftler),
        ("list(keys()) kopyası", onceki[3], sonraki[3], "c" in liste)):
    print(f"{ad:<22s}{once:>15d}{sonra:>7d}{str(icerir):>23s}")

print()
buyuk = {i: i for i in range(1000)}
gorunum = buyuk.keys()
anlik = list(buyuk.keys())
print(f"bin girdilik sözlükte görünümün yuvası yoktur; anlık kopyanın yuvası "
      f"{len(anlik)}")
print(f"gorunum is buyuk.keys() -> {gorunum is buyuk.keys()} | "
      f"gorunum == buyuk.keys() -> {gorunum == buyuk.keys()}")

print()
try:
    for anahtar in buyuk:
        if anahtar == 3:
            buyuk[1000] = 1000
except RuntimeError as e:
    print(f"görünüm üzerinde dolaşırken ekleme: {type(e).__name__}")
kopya_uzerinde = 0
for anahtar in list(buyuk):
    if anahtar == 3:
        buyuk[1001] = 1001
    kopya_uzerinde += 1
print(f"anlık kopya üzerinde dolaşırken ekleme: sorunsuz, "
      f"{kopya_uzerinde} tur döndü, sözlük {len(buyuk)} girdiye çıktı")

print()
sira = {"kuzey": 1, "yamac": 2, "ozet": 3}
print(f"ekleme sırası: {list(sira)}")
del sira["kuzey"]
sira["kuzey"] = 9
print(f"silinip yeniden eklendikten sonra: {list(sira)}")
sira["yamac"] = 20
print(f"var olan anahtarın değeri değişince: {list(sira)}")
```

```
kap                    eklemeden önce  sonra  yeni anahtarı görüyor
keys() görünümü                     2      3                   True
values() görünümü                   2      3                   True
items() görünümü                    2      3                   True
list(keys()) kopyası                2      2                  False

bin girdilik sözlükte görünümün yuvası yoktur; anlık kopyanın yuvası 1000
gorunum is buyuk.keys() -> False | gorunum == buyuk.keys() -> True

görünüm üzerinde dolaşırken ekleme: RuntimeError
anlık kopya üzerinde dolaşırken ekleme: sorunsuz, 1001 tur döndü, sözlük 1002 girdiye çıktı

ekleme sırası: ['kuzey', 'yamac', 'ozet']
silinip yeniden eklendikten sonra: ['yamac', 'ozet', 'kuzey']
var olan anahtarın değeri değişince: ['yamac', 'ozet', 'kuzey']
```

#### Pencere Kapanmıyor

Üst tablo dört satırın üçünü aynı yana koyuyor. Üç görünümün de uzunluğu **2**'den **3**'e
çıkıyor ve üçü de sonradan eklenen anahtarı **görüyor**. Anlık kopya **2**'de kalıyor ve
görmüyor. Görünümler ekleme anında var bile değillerdi — kurulduklarında sözlük iki
girdiliydi. Ama onların taşıdığı şey iki girdi değil, **sözlüğün kendisine giden bir bağdı**;
sözlük değişince gördükleri de değişti.

İkinci blok bunun bedelini ve kazancını yan yana koyuyor. Bin girdilik sözlükte anlık kopya
**1000** yuva açıyor — her biri bir anahtara giden bağ. Görünüm hiç yuva açmıyor; ne kadar
büyük sözlüğe bakarsa baksın taşıdığı şey tek bir bağdır. Bir sözlüğün anahtarları üzerinde
yalnız dolaşılacaksa anlık kopya çıkarmak bin yuvalık bir bedeldir ve karşılığında hiçbir şey
alınmaz. Görünüm nesnesinin kendisi her istendiğinde yeniden kuruluyor —
`gorunum is buyuk.keys()` **False** — ama iki görünüm eşit sayılıyor, çünkü ikisi de aynı
sözlüğe bakıyor.

Karşılığında ödenen şey üçüncü bloktadır. Görünüm üzerinde dolaşırken sözlüğe anahtar
eklemek `RuntimeError` veriyor: sözlük dolaşma sırasında büyüdüğünde görünümün nereye
baktığı belirsizleşir ve dil bunu bir kusur sayıp döngüyü keser. Aynı iş anlık kopya
üzerinde sorunsuz yürüyor — kopya çıkarıldığı andaki durumu taşır, sözlük sonradan ne olursa
olsun tur sayısı değişmez. İkinci ölçümün **1001** turla başlaması, kesilen ilk döngünün
istisnadan önce bir anahtar eklemiş olmasındandır.

Kural buradan çıkıyor: **dolaşırken değiştirilecekse anlık kopya alınır, yalnız okunacaksa
görünüm kullanılır.** Anlık kopyanın bedeli yuva, görünümün bedeli kırılganlıktır.

Son üç satır sıralamayı gösteriyor. Anahtarlar **ekleme sırasında** duruyor. Silinip yeniden
eklenen anahtar başa değil **sona** gidiyor, çünkü yeniden ekleme yeni bir girdidir. Var
olan bir anahtarın değerini değiştirmek ise sırayı hiç bozmuyor — girdinin yeri korunuyor,
yalnız değeri yenileniyor.

#### Birleştirmenin ve Aramanın Yazımları

Listelerde `+` ile `+=` ayrımı yeni kap kurmakla var olanı büyütmek arasındaydı. Sözlükte
aynı ayrım vardır ve beş ayrı yazımla karşımıza çıkar. Yanına bir de arama tarafı konur:
var olmayan bir anahtar istendiğinde beş yazım beş ayrı şey yapar.

- **KL37** — Beş biçim de aynı üç girdilik sözlüğün **tazeden kurulmuş** bir kopyasıyla
  başlar; değer nesneleri ölçümden önce bir kez var edilir.
- **KL38** — Sağdaki sözlük soldakiyle **bir anahtarda çakışır**; böylece birleşmenin
  anahtar sayısını nasıl bulduğu görünür.
- **KL39** — Arama tarafında düşen istisnanın **sınıf adı** yazılır; dönen değer varsa
  doğrudan basılır.

```python
"""Sozlugu birlestirmenin bicimleri ve var olmayan anahtarin bes yaziminda karsiligi."""

SAYAC = {"uretilen": 0}


class 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"]


DEGERLER = {ad: Oge(i) for i, ad in enumerate(("kuzey", "yamac", "ozet", "guney"))}
SOL = {"kuzey": DEGERLER["kuzey"], "yamac": DEGERLER["yamac"],
       "ozet": DEGERLER["ozet"]}
SAG = {"ozet": DEGERLER["guney"], "guney": DEGERLER["guney"]}


def birlestir(d):
    return d | SAG


def yerinde_birlestir(d):
    d |= SAG
    return d


def guncelle(d):
    d.update(SAG)
    return d


def kopyala(d):
    return dict(d)


def acarak_kur(d):
    return {**d, **SAG}


BICIMLER = (("d | e", birlestir), ("d |= e", yerinde_birlestir),
            ("d.update(e)", guncelle), ("dict(d)", kopyala),
            ("{**d, **e}", acarak_kur))

print(f"{'biçim':<14s}{'yeni öğe':>9s}{'aynı kap':>10s}{'anahtar':>9s}"
      f"{'değerler paylaşılıyor':>23s}")
for ad, islem in BICIMLER:
    d = dict(SOL)
    once = d
    sifirla()
    sonuc = islem(d)
    paylasim = all(any(v is x for x in DEGERLER.values())
                   for v in sonuc.values())
    print(f"{ad:<14s}{uretilen():>9d}{str(sonuc is once):>10s}"
          f"{len(sonuc):>9d}{str(paylasim):>23s}")

print()
birlesik = SOL | SAG
print(f"çakışan anahtar 'ozet' -> {birlesik['ozet']} | "
      f"sağdaki değerle aynı nesne: {birlesik['ozet'] is SAG['ozet']}")

print()
print(f"{'yazım':<24s}{'sonuç'}")
try:
    SOL["bozuk"]
except KeyError as e:
    print(f"{'d[yok]':<24s}{type(e).__name__}")
print(f"{'d.get(yok)':<24s}{SOL.get('bozuk')}")
print(f"{'d.get(yok, varsayılan)':<24s}{SOL.get('bozuk', Oge(9))}")
print(f"{'d.pop(yok, varsayılan)':<24s}{SOL.pop('bozuk', Oge(9))}")
print(f"{'yok in d':<24s}{'bozuk' in SOL}")
```

```
biçim          yeni öğe  aynı kap  anahtar  değerler paylaşılıyor
d | e                 0     False        4                   True
d |= e                0      True        4                   True
d.update(e)           0      True        4                   True
dict(d)               0     False        3                   True
{**d, **e}            0     False        4                   True

çakışan anahtar 'ozet' -> Oge(3) | sağdaki değerle aynı nesne: True

yazım                   sonuç
d[yok]                  KeyError
d.get(yok)              None
d.get(yok, varsayılan)  Oge(9)
d.pop(yok, varsayılan)  Oge(9)
yok in d                False
```

Beş biçimin beşi de **0** yeni değer var ediyor ve beşinde de değerler paylaşılıyor. Sözlük,
listenin yaptığını anahtar tarafında yapıyor: taşıdığı şey değerler değil, onlara giden
bağlar. Ayrım yine "aynı kap" sütununda — `d | e`, `dict(d)` ve `{**d, **e}` yeni bir kap
kuruyor, `d |= e` ile `d.update(e)` var olanı büyütüyor.

Anahtar sayısı üçle ikinin toplamı olan beş değil, **4**. Çakışan anahtar iki kez sayılmıyor;
sağdaki değer soldakini eziyor ve `birlesik['ozet'] is SAG['ozet']` bunu doğruluyor. Bir
sözlüğü birleştirmenin sonucu bu yüzden öngörülebilir bir sayı değildir — çakışma sayısına
bağlıdır ve çakışmayı ancak anahtarlar söyler.

Alt tablo var olmayan bir anahtarın beş yazımdaki karşılığını veriyor. Yalnız köşeli
ayraçla erişim **istisna** üretiyor; kalan dördü değer döndürüyor. `get` varsayılan
verilmediğinde `None` veriyor ve bu bir kusur bildirimi değildir — sözlükte `None` değerli
bir anahtar da bulunabilirdi, dolayısıyla `None` dönmesi "yok" demenin kesin yolu değildir.
Yokluğu kesin sınayan tek yazım üyelik sınamasıdır ve o da bu dersin ilk ölçümündeki
**1** ya da **0** karşılaştırmayla yapılır.

#### Özet

- Aynı iki yüz öğede üyelik sınaması listede **200**, kümede ve sözlükte **1** karşılaştırma
  yapar; bulunmayan öğe arandığında liste yine **200**, küme ve sözlük **0** yapar.
- Listenin sayısı konuma bağlıdır — ilk öğe **1**, ortadaki **101**, son öğe **200**; kümede
  üç arama da **1**'dir, çünkü küme yuvaları gezmez, hesaplar.
- `keys()`, `values()` ve `items()` birer görünümdür: sözlüğe bakmayı sürdürürler ve sonradan
  eklenen anahtarı görürler — uzunlukları **2**'den **3**'e çıkar. `list(...)` ile alınan
  anlık kopya **2**'de kalır.
- Görünüm yuva tutmaz; bin girdilik sözlüğün anlık kopyası **1000** yuva açar. Karşılığında
  görünüm üzerinde dolaşırken sözlüğe eklemek `RuntimeError` verir, anlık kopya üzerinde
  vermez.
- Anahtarlar ekleme sırasında durur; silinip yeniden eklenen anahtar sona gider, var olan bir
  anahtarın değerini değiştirmek sırayı bozmaz.

#### Sonraki Adım

Bu dersin ölçümünde küme, sözlüğün yanında sessizce durdu ve aynı sayıları verdi — **1** ve
**0**. Kendi başına ne yaptığı sorulmadı. Sonraki ders yalnız kümeye bakar ve onun en sık
kullanılan işini ölçer: bir listedeki tekrarları atmak. Soru yine sayıyla yanıtlanacak.
Tekrarları elle ayıklamak ile kümeye vermek kaç karşılaştırmaya ve kaç nesneye mal oluyor ve
kümeden geri dönen kapta öğeler yeni mi, eski mi?

### Kümeler

Source: https://academia.sh/tr/kurslar/python-veri-yapilari/kumeler

Üç yüz öğeyi tekilleştirmek elle tarandığında 15050, kümeye verildiğinde 200 karşılaştırma yapar ve üçünde de sonuç aynı yüz öğedir; altı küme işleminin altısı da 0 yeni öğe var eder ve dördü yeni kap kurar, ikisi var olanı değiştirir.

Önceki dersin ölçümünde küme, sözlüğün yanında sessizce durdu ve aynı sayıları verdi:
bulunan öğede **1**, bulunmayan öğede **0** karşılaştırma. Kendi başına ne yaptığı
sorulmadı.

**Küme (set)**, yalnız üyelik tutan kaptır — değeri yoktur, sırası yoktur, tekrarı yoktur.
Bu üç yokluk aynı düzeneğin sonucudur ve bu dersin ölçtüğü işi doğrudan mümkün kılar: bir
veri yığınındaki **tekrarları atmak**. Soru yine sayıyla yanıtlanacak. Tekrarları elle
ayıklamak ile kümeye vermek kaç karşılaştırmaya mal oluyor ve geri dönen kapta öğeler yeni
mi, eski mi?

#### Kümenin Sorusu

Küme bir öğeyi tutarken "kaçıncı sırada" diye sormaz; yalnız "bu zaten var mı" diye sorar.
Yanıtı, önceki derste sözlüğün anahtar için kullandığı yolla bulur: öğenin **karma değerinden** hangi
bölmeye düştüğünü hesaplar ve orada bakar. Bu yüzden kümenin öğeleri de karmalanabilir olmak
zorundadır; demetler dersinde ölçülen koşulun aynısı burada öğeler için geçerlidir.

Bu düzeneğin kendisi — karma tablosu, çakışma, yük çarpanı — **Veri Yapıları** kursunda
kuruldu ve tekrarlanmaz. Burada ölçülen şey, aynı işin iki yazımının kaç somut
karşılaştırmaya mal olduğudur; büyüme sınıfı iddiası değildir, belirli bir veriyle yapılmış
bir sayımdır. Karşılaştırmaları öğenin kendisi sayar.

Tekilleştirmenin elle yazımı doğrudan ve okunaklıdır: sonuç listesi tutulur, her öğe için
"bu listede var mı" diye bakılır, yoksa eklenir. Bu yazımın gizli maliyeti, "var mı"
sorusunun her seferinde **bir listeye** sorulmasıdır — ve önceki ders bir listeye sorulan
üyelik sorusunun neye mal olduğunu ölçmüştü.

- **KL40** — Ham veri üç yüz öğedir ve **yüz ayrı değer** taşır; her değer tam üç kez geçer.
  Beklenen sonuç bu yüzden önceden bilinir ve kâhin kurgudur.
- **KL41** — Üç yöntem de **aynı ham listeyle** çalışır; öğeler bir kez var edilir ve
  yöntemlerin hiçbiri yeni öğe kurmaz.
- **KL42** — Sayaç her yöntemden hemen önce sıfırlanır; ham verinin kurulması ölçüme girmez.
- **KL43** — "Yeni öğe" sütunu, sonuçtaki her öğenin ham veride `is` ile aranmasıyla bulunur;
  sıfır olması sonucun paylaşıldığı anlamına gelir.
- **KL44** — Sıra sütunu yalnız **sıra güvencesi olan** yöntemler için doldurulur; kümenin
  öğelerini hangi düzende verdiği ölçülmez ve ona dayanılmaz.

```python
"""Tekillestirme: uc yontem, ayni sonuc, ayri sayida karsilastirma."""


class Sayan:
    """Esitlik karsilastirmalarini sayan deger."""

    sayac = {"karsilastirma": 0}

    def __init__(self, d):
        self.d = d

    def __eq__(self, o):
        Sayan.sayac["karsilastirma"] += 1
        return isinstance(o, Sayan) and self.d == o.d

    def __hash__(self):
        return hash(self.d)


BENZERSIZ = 100
HAM = [Sayan(i % BENZERSIZ) for i in range(3 * BENZERSIZ)]


def elle(ham):
    sonuc = []
    for x in ham:
        if x not in sonuc:
            sonuc.append(x)
    return sonuc


def kumeye_ver(ham):
    return set(ham)


def anahtar_yap(ham):
    return dict.fromkeys(ham)


print(f"{'yöntem':<22s}{'karşılaştırma':>14s}{'benzersiz':>10s}"
      f"{'yeni öğe':>9s}{'sıra korunur':>14s}")
ILK = None
for ad, yontem, sirali in (("elle tarama", elle, True),
                           ("set(ham)", kumeye_ver, False),
                           ("dict.fromkeys(ham)", anahtar_yap, True)):
    Sayan.sayac["karsilastirma"] = 0
    sonuc = yontem(HAM)
    sayi = Sayan.sayac["karsilastirma"]
    yeni = sum(1 for x in sonuc if not any(x is y for y in HAM))
    if ILK is None:
        ILK = list(sonuc)
    ayni_sira = list(sonuc) == ILK
    print(f"{ad:<22s}{sayi:>14d}{len(sonuc):>10d}{yeni:>9d}"
          f"{str(ayni_sira) if sirali else 'güvence yok':>14s}")

print()
print(f"ham veri {len(HAM)} öğe | tekrar {len(HAM) - BENZERSIZ} | "
      f"benzersiz {BENZERSIZ}")
print(f"elle taramanın sonucundaki öğeler ham veriden mi: "
      f"{all(any(x is y for y in HAM) for x in elle(HAM))}")
```

```
yöntem                 karşılaştırma benzersiz yeni öğe  sıra korunur
elle tarama                    15050       100        0          True
set(ham)                         200       100        0   güvence yok
dict.fromkeys(ham)               200       100        0          True

ham veri 300 öğe | tekrar 200 | benzersiz 100
elle taramanın sonucundaki öğeler ham veriden mi: True
```

#### Aynı Yüz Öğe, Üç Ayrı Fatura

Üç satırın "benzersiz" sütunu aynı: **100**. Üç yöntem de doğru sonucu veriyor ve üçü de
**0** yeni öğe var ediyor — sonuçtaki nesnelerin hepsi ham verinin nesneleridir. Fark
tamamen soldaki sütunda.

Elle tarama **15050** karşılaştırma yapıyor. Bu sayı iki parçadan gelir. İlk yüz öğe hep yeni
olduğu için her biri o ana kadar birikmiş sonucun tamamıyla karşılaştırılır; bu **4950**
eder. Kalan iki yüz öğe tekrardır ve her biri eşini bulunca durur; bunlar da **10100** eder.
Toplam sayı, bir listeye sorulan üyelik sorusunun **kaç kez** sorulduğuyla değil, her sorunun
**kaç yuvayı** geçtiğiyle büyüyor.

Kümeye vermek aynı işi **200** karşılaştırmayla bitiriyor — yaklaşık yetmiş beşte biri.
İkisinin arasındaki fark bir başka yazımdan değil, bir başka **soru sorma biçiminden**
geliyor: elle tarama her seferinde bütün sonucu geziyor, küme her seferinde tek bir bölmeye
bakıyor. Ve 200 sayısının kendisi de anlamlıdır: ham veride tam **200** tekrar var. Küme her
tekrar için bir karşılaştırma yapıyor, benzersiz öğeler için hiç yapmıyor. Öğe yeniyse
bölmesi boştur ve karşılaştırılacak bir şey yoktur.

Üçüncü satır kümenin ödettiği bedeli ve onu ödemeden kurtulmanın yolunu birlikte veriyor.
Küme sırasızdır; öğelerini hangi düzende verdiği bu derste ölçülmez ve ona dayanılmaz. Ama
anahtarları ekleme sırasında tutan bir eşleme, aynı tekilleştirmeyi **aynı 200
karşılaştırmayla** yapıp sırayı da koruyor. Sonucun sıraya ihtiyacı varsa doğru araç küme
değil, anahtar olarak kullanılan bir eşlemedir.

#### Küme İşlemleri

Kümenin ikinci işi kümeler arası hesaptır: birleşim, kesişim, fark, simetrik fark. Bu dört
işlem küme kuramından gelir ve anlamları tartışmasızdır. Ölçülecek olan şey anlamları değil,
**neye mal oldukları**: kaç öğe var ediyorlar, sonuç yeni bir kap mı ve sonucun içindeki
nesneler nereden geliyor.

- **KL45** — Altı biçim de aynı üç öğelik kümenin **tazeden kurulmuş** bir kopyasıyla başlar;
  öğeler ölçümden önce bir kez var edilir.
- **KL46** — İki küme **bir öğede kesişir**; kesişim ve fark sayıları bu yüzden önceden
  bilinir ve tabloyu doğrular.
- **KL47** — "Öğeler havuzdan" sütunu, sonuçtaki her öğenin ölçüm öncesi havuzda `is` ile
  aranmasıyla bulunur.
- **KL48** — Karmalanabilirlik doğrudan sınanır ve düşen istisnanın **sınıf adı** yazılır;
  karma değeri basılmaz.

```python
"""Kume islemleri: her biri yeni kap kurar, hicbiri yeni oge var etmez."""

SAYAC = {"uretilen": 0}


class 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"]


HAVUZ = [Oge(i) for i in range(5)]
SOL = {HAVUZ[0], HAVUZ[1], HAVUZ[2]}
SAG = {HAVUZ[2], HAVUZ[3], HAVUZ[4]}


def birlesim(a):
    return a | SAG


def kesisim(a):
    return a & SAG


def fark(a):
    return a - SAG


def simetrik_fark(a):
    return a ^ SAG


def yerinde_birlesim(a):
    a |= SAG
    return a


def yerinde_kesisim(a):
    a &= SAG
    return a


BICIMLER = (("a | b", birlesim), ("a & b", kesisim), ("a - b", fark),
            ("a ^ b", simetrik_fark), ("a |= b", yerinde_birlesim),
            ("a &= b", yerinde_kesisim))

print(f"{'işlem':<10s}{'yeni öğe':>9s}{'aynı kap':>10s}{'öğe':>5s}"
      f"{'öğeler havuzdan':>17s}")
for ad, islem in BICIMLER:
    a = set(SOL)
    once = a
    sifirla()
    sonuc = islem(a)
    havuzdan = all(any(x is y for y in HAVUZ) for x in sonuc)
    print(f"{ad:<10s}{uretilen():>9d}{str(sonuc is once):>10s}{len(sonuc):>5d}"
          f"{str(havuzdan):>17s}")

print()
print(f"{'sınama':<30s}{'sonuç'}")
print(f"{'SOL <= SOL | SAG':<30s}{SOL <= (SOL | SAG)}")
print(f"{'SOL < SOL':<30s}{SOL < SOL}")
print(f"{'SOL.isdisjoint(SAG)':<30s}{SOL.isdisjoint(SAG)}")
print(f"{'(SOL - SAG).isdisjoint(SAG)':<30s}{(SOL - SAG).isdisjoint(SAG)}")

print()
DONMUS = frozenset({HAVUZ[0], HAVUZ[1]})
print(f"dondurulmuş küme, kümenin öğesi olabilir -> {DONMUS in {DONMUS}}")
for aday in (SOL, DONMUS):
    try:
        {aday: 0}
        sonuc = "evet"
    except TypeError as e:
        sonuc = type(e).__name__
    print(f"{type(aday).__name__:<12s} anahtar olabilir -> {sonuc}")
try:
    {[1, 2]}
except TypeError as e:
    print(f"liste küme öğesi olabilir -> {type(e).__name__}")
```

```
işlem      yeni öğe  aynı kap  öğe  öğeler havuzdan
a | b             0     False    5             True
a & b             0     False    1             True
a - b             0     False    2             True
a ^ b             0     False    4             True
a |= b            0      True    5             True
a &= b            0      True    1             True

sınama                        sonuç
SOL <= SOL | SAG              True
SOL < SOL                     False
SOL.isdisjoint(SAG)           False
(SOL - SAG).isdisjoint(SAG)   True

dondurulmuş küme, kümenin öğesi olabilir -> True
set          anahtar olabilir -> TypeError
frozenset    anahtar olabilir -> evet
liste küme öğesi olabilir -> TypeError
```

#### Dört Hesap, Sıfır Öğe

Altı satırın "yeni öğe" sütunu tamamen **0** ve "öğeler havuzdan" sütunu tamamen **True**.
Küme işlemleri hesap yapar, üretim yapmaz: sonuçta görünen her nesne ölçümden önce var
edilmiş nesnelerden biridir. Bu, listelerde ve sözlüklerde ölçülen örüntünün üçüncü kez
tekrarıdır — Python'un yerleşik kapları öğeleri taşımaz, onlara giden bağları düzenler.

Öğe sayıları kurguyu doğruluyor: üç ve üç öğelik, bir öğede kesişen iki kümenin birleşimi
**5**, kesişimi **1**, farkı **2**, simetrik farkı **4**. Toplamların tutması ölçümün kendi
iç denetimidir.

Ayrım yine "aynı kap" sütununda ve artık tanıdık: işleç biçimi **yeni kap** kuruyor, atamalı
biçim **var olanı** değiştiriyor. Bu, bu kursta dördüncü kez aynı yerde çıkan ayrımdır —
listede `+` ile `+=`, demette aynı ikilinin zorunlu yeniden kurması, sözlükte `|` ile `|=`,
kümede `|` ile `|=`. Kural kaba özgü değil, dile özgüdür: **işleç yeni nesne verir, atamalı
işleç var olanı değiştirmeye çalışır.**

Alt bloktaki alt küme sınamaları kümenin karşılaştırma anlamını veriyor. `<=` alt küme
sorar ve bir küme kendisinin alt kümesidir; `<` **öz** alt küme sorar ve kendisi için
**False** verir. Bu ikisi sayılarla değil, kümelerin kapsamıyla ilgilidir ve sayı
karşılaştırmasıyla karıştırılmamalıdır.

Son blok değişmezlik ölçümünü kümeye taşıyor. Bir küme kendisi **değiştirilebilir** olduğu
için karmalanamaz ve anahtar olamaz; `TypeError` ile düşüyor. **Dondurulmuş küme (frozenset)**
aynı öğeleri değişmez bir kapta tutar ve hem anahtar olabiliyor hem başka bir kümenin öğesi
olabiliyor. Liste ise ne anahtar ne öğe olabiliyor. Demetler dersinde kurulan kural burada da
aynen işliyor: bir kabın başka bir kaba **konabilmesinin** koşulu, kendi içeriğinin sabit
kalacağına dair verdiği sözdür.

#### Aynı Hesabı Listeyle Yapmak

Küme işlemlerinin hepsi listelerle de yazılabilir; hiçbiri kümeye özgü bir yetenek
gerektirmez. Kesişim "soldakilerden sağdakinde de bulunanlar", fark ise "soldakilerden
sağdakinde bulunmayanlar" demektir ve ikisi de tek satırlık bir süzme ile yazılır. Bu
yazımların sonucu doğrudur. Ölçülecek olan şey doğruluk değil, **fiyattır**.

- **KL49** — İki koleksiyon **iki yüzer öğedir** ve **yüz öğede** çakışır; kesişim ve fark
  sonuçlarının yüzer öğe olması bu kurgudan bilinir ve tabloyu doğrular.
- **KL50** — Liste ve küme biçimleri **aynı nesnelerden** kurulur; sayaç her satırdan hemen
  önce sıfırlanır ve kapların kurulması ölçüme girmez.
- **KL51** — Son blok bir sayı ölçmez; kümenin **yanıtlayabildiği** ile
  **yanıtlayamadığı** soruyu ayırır.

```python
"""Kesisimi elle almak ile kumeye vermek; ve tekillestirmenin dusurdugu bilgi."""


class Sayan:
    sayac = {"karsilastirma": 0}

    def __init__(self, d):
        self.d = d

    def __eq__(self, o):
        Sayan.sayac["karsilastirma"] += 1
        return isinstance(o, Sayan) and self.d == o.d

    def __hash__(self):
        return hash(self.d)


SOL_LISTE = [Sayan(i) for i in range(200)]
SAG_LISTE = [Sayan(i) for i in range(100, 300)]
SOL_KUME, SAG_KUME = set(SOL_LISTE), set(SAG_LISTE)


def elle_kesisim():
    return [x for x in SOL_LISTE if x in SAG_LISTE]


def kume_kesisim():
    return SOL_KUME & SAG_KUME


def elle_fark():
    return [x for x in SOL_LISTE if x not in SAG_LISTE]


def kume_fark():
    return SOL_KUME - SAG_KUME


print(f"{'işlem':<28s}{'karşılaştırma':>14s}{'sonuç öğesi':>13s}")
for ad, islem in (("kesişim, iki listeyle", elle_kesisim),
                  ("kesişim, iki kümeyle", kume_kesisim),
                  ("fark, iki listeyle", elle_fark),
                  ("fark, iki kümeyle", kume_fark)):
    Sayan.sayac["karsilastirma"] = 0
    sonuc = islem()
    print(f"{ad:<28s}{Sayan.sayac['karsilastirma']:>14d}{len(sonuc):>13d}")

print()
HAM = [Sayan(i % 100) for i in range(300)]
KUME = set(HAM)
print(f"ham veri {len(HAM)} öğe -> küme {len(KUME)} öğe")
print(f"kümeye sorulabilen soru: 'var mı' -> {Sayan(7) in KUME}")
print("kümeye sorulamayan soru: 'kaç kez geçti'")
```

```
işlem                        karşılaştırma  sonuç öğesi
kesişim, iki listeyle                25050          100
kesişim, iki kümeyle                   100          100
fark, iki listeyle                   25050          100
fark, iki kümeyle                      100          100

ham veri 300 öğe -> küme 100 öğe
kümeye sorulabilen soru: 'var mı' -> True
kümeye sorulamayan soru: 'kaç kez geçti'
```

Dört satırın "sonuç öğesi" sütunu doğruluğu bitiriyor: dördü de **100** öğe veriyor. Liste
yazımı yanlış değil, yalnız pahalı. Kesişimi listelerle almak **25050**, kümelerle almak
**100** karşılaştırma yapıyor — iki yüz elli katı bir fark, aynı veride ve aynı sonuçla.

Fark satırları aynı sayıyı veriyor ve bu bir rastlantı değil. Liste yazımında her öğe için
sağdaki liste taranıyor; taramanın "bulundu" ya da "bulunamadı" ile bitmesi, kaç yuva
geçildiğini belirlemez. Kümede de aynı simetri var: kesişim eşleşen her öğe için bir
karşılaştırma yapıyor, fark da aynısını. Erişim biçimi değişince maliyetin **şekli**
değişiyor, yalnız büyüklüğü değil.

Son blok kümenin sınırını çiziyor ve bu ders için son sözdür. Üç yüz öğelik veri kümeye
verildiğinde **100** öğeye iniyor. Kaybolan şey iki yüz nesne değil — nesneler duruyor, ham
liste onları hâlâ tutuyor. Kaybolan şey **bilgidir**: hangi değerin kaç kez geçtiği. Küme
"var mı" sorusunu bir karşılaştırmayla yanıtlar, ama "kaç kez" sorusunu hiç yanıtlayamaz,
çünkü o sayıyı hiç tutmaz. Tekilleştirme ucuzdur; ucuzluğunun kaynağı, atılan bilginin geri
istenmeyeceği varsayımıdır.

#### Özet

- Üç yüz öğeli veride tekilleştirme elle tarandığında **15050**, kümeye verildiğinde **200**
  karşılaştırma yapar; üç yöntem de aynı **100** benzersiz öğeyi ve **0** yeni öğe verir.
- Kümenin **200** sayısı ham verideki tekrar sayısına eşittir: her tekrar bir karşılaştırma
  ister, benzersiz öğe hiç istemez.
- Küme sırasızdır; sıra korunacaksa aynı **200** karşılaştırmayı yapıp ekleme sırasını da
  tutan bir eşleme kullanılır.
- Altı küme işleminin altısı da **0** öğe var eder ve sonuçtaki her nesne eskidir; işleç
  biçimi yeni kap kurar, atamalı biçim var olanı değiştirir.
- Küme değiştirilebilir olduğu için anahtar olamaz; dondurulmuş küme hem anahtar hem küme
  öğesi olabilir — koşul, kabın içeriğinin sabit kalacağına dair verdiği sözdür.
- Aynı kesişimi listelerle almak **25050**, kümelerle almak **100** karşılaştırma yapar;
  ikisi de aynı **100** öğeyi verir, ama tekilleştirme "kaç kez geçti" bilgisini düşürür.

#### Sonraki Adım

Bu dersin son satırı kümenin yanıtlayamadığı soruyu yazdı: **kaç kez.** O soruyu tutmak
isteyen herkes aynı kalıbı elle yazar — bir sözlük açar, anahtar varsa artırır, yoksa birden
başlatır. Aynı biçimde elle yazılan başka kalıplar da vardır: bir listenin iki ucundan da
öğe almak, olmayan anahtar için boş bir kap hazırlamak. Sonraki ders bu kalıpların standart
kitaplıktaki hazır karşılıklarına bakar ve her birini elle yazılan eşdeğeriyle yan yana
koyar. Ölçülecek iki sayı var: kaç satır ve kaç nesne.

### Özelleşmiş Koleksiyonlar

Source: https://academia.sh/tr/kurslar/python-veri-yapilari/ozellesmis-koleksiyonlar

Aynı gruplamayı setdefault ile yazmak 320, varsayılan sözlükle yazmak 100 kap var eder; bin öğelik akışta üç yazım da 1000 nesne üretir ama tutulan 1000'e karşı 5'tir ve döndürme listede üç kap kurarken kuyrukta hiç kurmaz.

Önceki dersin son satırı kümenin yanıtlayamadığı soruyu yazdı: **kaç kez.** Küme yalnız
üyelik tutar; sayıyı tutmaz, çünkü tutması gerekmez.

O sayıyı isteyen herkes aynı kalıbı elle yazar — bir sözlük açar, anahtar varsa artırır,
yoksa birden başlatır. Aynı biçimde elle yazılan başka kalıplar da vardır: kayıtları anahtara
göre kutulara ayırmak, bir akışın yalnız son birkaç öğesini elde tutmak, bir dizinin ucunu
öbür ucuna taşımak. Bu ders o kalıpların standart kitaplıktaki hazır karşılıklarına bakar ve
her birini elle yazılan eşdeğeriyle yan yana koyar. Ölçülecek iki sayı var: **kaç satır** ve
**kaç nesne.**

#### Kalıbın İki Fiyatı

Bir kalıbın elle yazılmasının iki ayrı bedeli vardır ve bunlar bağımsızdır. Birincisi
**satırdır**: yazılan, okunan, sınanan ve yanlış yazılabilen kod. İkincisi **nesnedir**:
kalıbın çalışırken var ettiği ve tuttuğu şeyler. Bir yazım satırı azaltıp nesneyi
artırabilir; ölçümdeki üç gruplama yazımından biri tam olarak bunu yapar.

Yığıt, kuyruk ve bağlı liste gibi yapıların kuramı **Veri Yapıları** kursunda kuruldu ve
burada tekrarlanmaz. Burada ölçülen şey Python'un o kuyruğu **hangi kapla verdiği** ve elle
yazılan karşılığına göre kaç satır ve kaç nesne tasarruf ettiğidir.

Satır sayısı elle sayılmaz: her yazım bir işlevin gövdesidir ve gövdenin boş olmayan satır
sayısı işlevin kaynağından okunur. Sayı böylece bir iddia değil, koşumun bulgusu olur.

- **KL52** — Gövde satırı işlevin kaynağından okunur; **tanım satırı ve boş satırlar
  sayılmaz.** Her yazım aynı biçimde girintilenmiş ve aynı işi yapan bir işlevdir.
- **KL53** — Üç sayma yazımı ve üç gruplama yazımı **aynı veriyle** çalışır; veri 320 kayıt
  ve 100 ayrı anahtar taşır, bir anahtar bilerek daha sık geçer.
- **KL54** — Doğruluk sütunu, sonucun elle yazılan ilk yazımın sonucuyla karşılaştırılmasıdır;
  kâhin elle yazılan kalıptır.
- **KL55** — Gruplamada kutu bir nesnedir ve her kuruluşunda sayılır; sayılan şey kutular,
  içlerine konan kayıtlar değildir.
- **KL56** — Sayaç her yazımdan hemen önce sıfırlanır; verinin kurulması ölçüme girmez.

```python
"""Sayma ve gruplama: elle yazilan kalip ile hazir karsiligi yan yana."""

import inspect
from collections import Counter, defaultdict

SAYAC = {"uretilen": 0}


class 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"]


VERI = [f"a{i % 100:02d}" for i in range(300)] + ["a07"] * 20


def govde_satiri(f):
    """Islev govdesinin bos olmayan satir sayisi; tanim satiri sayilmaz."""
    satirlar = inspect.getsource(f).splitlines()[1:]
    return sum(1 for s in satirlar if s.strip())


def elle_say(veri):
    sayim = {}
    for k in veri:
        if k in sayim:
            sayim[k] += 1
        else:
            sayim[k] = 1
    return sayim


def get_ile_say(veri):
    sayim = {}
    for k in veri:
        sayim[k] = sayim.get(k, 0) + 1
    return sayim


def sayacla(veri):
    return Counter(veri)


def elle_grupla(veri):
    kutular = {}
    for i, k in enumerate(veri):
        if k not in kutular:
            kutular[k] = Oge([])
        kutular[k].deger.append(i)
    return kutular


def setdefault_ile_grupla(veri):
    kutular = {}
    for i, k in enumerate(veri):
        kutular.setdefault(k, Oge([])).deger.append(i)
    return kutular


def varsayilan_sozlukle_grupla(veri):
    kutular = defaultdict(lambda: Oge([]))
    for i, k in enumerate(veri):
        kutular[k].deger.append(i)
    return kutular


BEKLENEN_SAYIM = elle_say(VERI)
BEKLENEN_GRUP = {k: v.deger for k, v in elle_grupla(VERI).items()}

print(f"{'saymanın yazımı':<24s}{'gövde satırı':>13s}{'var edilen':>11s}"
      f"{'sonuç doğru':>12s}")
for ad, yontem in (("elle sınama", elle_say), ("get ile", get_ile_say),
                   ("sayaç", sayacla)):
    sifirla()
    sonuc = yontem(VERI)
    print(f"{ad:<24s}{govde_satiri(yontem):>13d}{uretilen():>11d}"
          f"{str(dict(sonuc) == BEKLENEN_SAYIM):>12s}")

print()
print(f"{'gruplamanın yazımı':<24s}{'gövde satırı':>13s}{'var edilen':>11s}"
      f"{'sonuç doğru':>12s}")
for ad, yontem in (("elle sınama", elle_grupla),
                   ("setdefault ile", setdefault_ile_grupla),
                   ("varsayılan sözlük", varsayilan_sozlukle_grupla)):
    sifirla()
    sonuc = yontem(VERI)
    dogru = {k: v.deger for k, v in sonuc.items()} == BEKLENEN_GRUP
    print(f"{ad:<24s}{govde_satiri(yontem):>13d}{uretilen():>11d}"
          f"{str(dogru):>12s}")

print()
print(f"veri {len(VERI)} kayıt | ayrı anahtar {len(BEKLENEN_SAYIM)} | "
      f"görülen sayılar {sorted(set(BEKLENEN_SAYIM.values()))}")
print(f"sayacın en çok geçen ikisi: {Counter(VERI).most_common(2)}")
```

```
saymanın yazımı          gövde satırı var edilen sonuç doğru
elle sınama                         7          0        True
get ile                             4          0        True
sayaç                               1          0        True

gruplamanın yazımı       gövde satırı var edilen sonuç doğru
elle sınama                         6        100        True
setdefault ile                      4        320        True
varsayılan sözlük                   4        100        True

veri 320 kayıt | ayrı anahtar 100 | görülen sayılar [3, 23]
sayacın en çok geçen ikisi: [('a07', 23), ('a00', 3)]
```

#### Satır Ucuz, Nesne Değil

Üst tablo yalnız satır tarafını ölçüyor ve tek yönlü. Elle sınamalı sayma **7** satır, `get`
ile yazımı **4**, **sayaç (Counter)** ise **1** satır. Üçü de doğru sonucu veriyor ve üçü de
**0** nesne var ediyor — sayılar tam sayıdır, kap değildir. Kazanılan şey yalnız satırdır;
ama satır da bir maliyettir, çünkü anahtarın ilk görülüşünü ayrı ele almak bir dal ve
yanlış yazılabilecek bir yer daha demektir.

Sayacın verdiği fazladan şey son satırda görünüyor: en çok geçen anahtarları sıralı vermek.
Elle yazımda bu, sözlüğü değere göre sıralayıp ilk ikisini almak demektir. Hazır kabın
sunduğu şey yalnız kalıbın kendisi değil, etrafındaki işlerdir.

Alt tablo asıl ölçümdür ve iki sütun ters yönde çalışıyor. Elle sınamalı gruplama **6**
satır ve **100** kutu var ediyor: her ayrı anahtar için bir kez. `setdefault` ile yazım
**4** satıra iniyor — ama **320** kutu var ediyor. Fark yazımın kendisindedir:
`setdefault` bir **çağrıdır** ve çağrının argümanı, sonuç kullanılacak mı diye bakılmadan,
çağrıdan **önce** değerlendirilir. Anahtar zaten varsa var edilen kutu hiç kullanılmaz ve
oracıkta atılır. Üç yüz yirmi kayıt için üç yüz yirmi kutu kurulur, iki yüz yirmisi boşuna.

**Varsayılan sözlük (defaultdict)** aynı **4** satırla **100** kutuya iniyor. Çünkü orada
kutuyu kuran şey çağıran taraf değil, kabın kendisidir: anahtar **gerçekten eksikse**
üretici çağrılır, değilse çağrılmaz. Aynı satır sayısı, üçte birinden az nesne.

Buradan çıkan kural yazıma değil, **değerlendirme anına** bakar: bir varsayılan değer
argüman olarak yazıldığında her çağrıda var edilir, üretici olarak verildiğinde yalnız
gerektiğinde. İki yazım aynı uzunlukta ve aynı sonuçtadır; ayrıldıkları yer bu kursun
ölçtüğü eksendir.

#### Akışın Yalnız Sonunu Tutmak

İkinci ölçüm kabın **tutma** tarafına bakar. Bin öğelik bir akışta her öğe var edilir —
bundan kaçış yoktur, çünkü öğeleri üreten şey kap değildir. Değişebilen tek şey, kabın
bunların **kaçını** elde tuttuğudur. Ölçüme giren üçüncü kap **çift uçlu kuyruktur
(deque)**: iki ucundan da öğe alınıp verilebilen, kurulurken bir uzunluk sınırı
verilebilen bir sıralı kap.

- **KL57** — Üç yazım da aynı bin öğelik akışı kurar; öğeler ölçüm sırasında var edilir ve
  sayaç her yazımdan önce sıfırlanır.
- **KL58** — Pencere beş öğedir ve son öğe her üç yazımda da yazdırılarak üçünün de akışı
  sonuna kadar tükettiği doğrulanır.
- **KL59** — Döndürme ölçümünde kurulan kap sayısı **koddaki kap kuran ifadelerden** okunur:
  iki dilim ve bir birleşim üç kap eder, yerinde döndürme hiç kap kurmaz. Kap sayısı ayrıca
  `is` ile doğrulanır.
- **KL60** — İki döndürme sonucu **aynı sırada** olup olmadığıyla karşılaştırılır; yazımların
  eşdeğerliği bu satırla gösterilir.
- **KL61** — Öğelerin paylaşımı, sonuçtaki her öğenin havuzda `is` ile aranmasıyla sınanır.

```python
"""Cift uclu kuyruk: sinirli pencere ve dondurme, elle yazilan karsiligiyla."""

import inspect
from collections import deque

SAYAC = {"uretilen": 0}


class 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 govde_satiri(f):
    satirlar = inspect.getsource(f).splitlines()[1:]
    return sum(1 for s in satirlar if s.strip())


AKIS, PENCERE = 1000, 5


def listeyle_kirpmasiz():
    kap = []
    for i in range(AKIS):
        kap.append(Oge(i))
    return kap


def listeyle_kirparak():
    kap = []
    for i in range(AKIS):
        kap.append(Oge(i))
        if len(kap) > PENCERE:
            del kap[0]
    return kap


def kuyrukla():
    kap = deque(maxlen=PENCERE)
    for i in range(AKIS):
        kap.append(Oge(i))
    return kap


print(f"{'yazım':<24s}{'gövde satırı':>13s}{'var edilen':>11s}"
      f"{'tutulan':>8s}{'son öğe':>10s}")
for ad, yontem in (("liste, kırpmasız", listeyle_kirpmasiz),
                   ("liste, elle kırpma", listeyle_kirparak),
                   ("çift uçlu kuyruk", kuyrukla)):
    sifirla()
    kap = yontem()
    print(f"{ad:<24s}{govde_satiri(yontem):>13d}{uretilen():>11d}"
          f"{len(kap):>8d}{str(kap[-1]):>10s}")

print()
HAVUZ = [Oge(i) for i in range(5)]
K = 2

liste = list(HAVUZ)
sifirla()
sol, sag = liste[K:], liste[:K]
dondurulmus = sol + sag
print(f"listeyle döndürme: var edilen {uretilen()} | kurulan kap 3 "
      f"(iki dilim ve birleşim)")
print(f"  sonuç {dondurulmus} | dondurulmus is liste -> "
      f"{dondurulmus is liste}")

kuyruk = deque(HAVUZ)
once = kuyruk
sifirla()
kuyruk.rotate(-K)
print(f"kuyrukla döndürme: var edilen {uretilen()} | kurulan kap 0")
print(f"  sonuç {list(kuyruk)} | kuyruk is once -> {kuyruk is once}")
print(f"  iki sonuç aynı sırada -> {list(kuyruk) == dondurulmus}")
print(f"  öğeler paylaşılıyor -> "
      f"{all(any(x is y for y in HAVUZ) for x in kuyruk)}")
```

```
yazım                    gövde satırı var edilen tutulan   son öğe
liste, kırpmasız                    4       1000    1000  Oge(999)
liste, elle kırpma                  6       1000       5  Oge(999)
çift uçlu kuyruk                    4       1000       5  Oge(999)

listeyle döndürme: var edilen 0 | kurulan kap 3 (iki dilim ve birleşim)
  sonuç [Oge(2), Oge(3), Oge(4), Oge(0), Oge(1)] | dondurulmus is liste -> False
kuyrukla döndürme: var edilen 0 | kurulan kap 0
  sonuç [Oge(2), Oge(3), Oge(4), Oge(0), Oge(1)] | kuyruk is once -> True
  iki sonuç aynı sırada -> True
  öğeler paylaşılıyor -> True
```

#### Var Edilen Aynı, Tutulan Ayrı

Üç satırın "var edilen" sütunu aynı: **1000**. Bu sütunun değişmemesi ölçümün en önemli
bulgusudur. Kabı değiştirmek, üretilen nesne sayısını değiştirmez — öğeleri kap üretmiyor.
Kabın seçtiği tek şey, üretilenlerin kaçının **elde kalacağıdır**.

"Tutulan" sütunu ayrılıyor: kırpmasız liste **1000** öğeyi tutuyor, elle kırpılan liste
**5**, çift uçlu kuyruk **5**. Üçü de akışı sonuna kadar tüketiyor ve üçünün de son öğesi
aynı. Yalnız son beş öğe gerekiyorsa kırpmasız liste dokuz yüz doksan beş nesneyi hiçbir
gerekçe olmadan elde tutuyor demektir.

Satır sütunu ikinci farkı veriyor: elle kırpma **6** satır ister, kuyruk **4**. Aradaki iki
satır bir koşul ve bir silmedir. Elle yazımda pencere sınırı **döngünün içinde** her turda
sınanan bir şeydir; kuyrukta kabın **tanımının** parçasıdır.

Alt blok döndürmeyi ölçüyor ve iki sonucun **aynı sırada** olduğunu doğruluyor. Listeyle
döndürme **0** öğe var ediyor ama **3** kap kuruyor: sağ parçanın dilimi, sol parçanın
dilimi ve ikisinin birleşimi. Sonuç yeni bir kaptır ve `dondurulmus is liste` **False**.
Kuyrukla döndürme **0** öğe ve **0** kap kuruyor; sonuç aynı kaptır ve `kuyruk is once`
**True**. Öğeler her iki yazımda da havuzdan geliyor.

Üç kap ile sıfır kap arasındaki fark küçük görünür, ama döndürme bir döngü içindeyse her tur
üç kap kurar. **Elle yazılan kalıp çoğu zaman doğru sonucu verir; ödettiği şey sonucun
kendisi değil, sonuca giderken kurulan ara kaplardır.**

#### Eksik Anahtar ve Korunan Sayı

İki özelleşmiş kap da bir sözlüktür ve ikisi de eksik anahtarda sözlükten ayrılır — ama
birbirlerinden de ayrılırlar. Üçüncü ölçüm köşeli ayraçla erişimi üç kapta yineler ve
erişimden **sonra** kaç anahtar kaldığına bakar.

- **KL62** — Üç kap da tek bir anahtarla, aynı değerle kurulur; ölçülen tek şey **var
  olmayan** bir anahtara erişimin sonucudur.
- **KL63** — Anahtar sayısı erişimden **sonra** okunur; erişimin kabı değiştirip
  değiştirmediği bu sayıyla görünür.
- **KL64** — Sayaç aritmetiği iki küçük sayaçla yapılır; sonuçlar anahtara göre sıralanarak
  basılır, çünkü ölçülen şey sıra değil, korunan sayıdır.

```python
"""Eksik anahtarda uc kap uc ayri sey yapar; ve sayac 'kac kez'i koruyarak hesaplar."""

from collections import Counter, defaultdict

SOZLUK = {"a07": 3}
VARSAYILAN = defaultdict(int, {"a07": 3})
SAYAC = Counter({"a07": 3})

print(f"{'kap':<20s}{'d[yok] sonucu':<16s}{'sonra kaç anahtar'}")
for ad, kap in (("sözlük", SOZLUK), ("varsayılan sözlük", VARSAYILAN),
                ("sayaç", SAYAC)):
    try:
        sonuc = str(kap["yok"])
    except KeyError as e:
        sonuc = type(e).__name__
    print(f"{ad:<20s}{sonuc:<16s}{len(kap)}")

print()
SOL = Counter("aabbbcc")
SAG = Counter("abbdd")
print(f"sol sayaç {dict(sorted(SOL.items()))}")
print(f"sağ sayaç {dict(sorted(SAG.items()))}")
print(f"{'işlem':<22s}{'sonuç'}")
for ad, sonuc in (("sayaç kesişimi", SOL & SAG), ("sayaç birleşimi", SOL | SAG),
                  ("sayaç farkı", SOL - SAG)):
    print(f"{ad:<22s}{dict(sorted(sonuc.items()))}")
print(f"{'küme kesişimi':<22s}{sorted(set(SOL) & set(SAG))}")
print(f"{'küme birleşimi':<22s}{sorted(set(SOL) | set(SAG))}")

print()
print(f"sayaç bir sözlüktür -> {isinstance(SOL, dict)} | "
      f"toplam kayıt {sum(SOL.values())} | ayrı anahtar {len(SOL)}")
print(f"kesişimin toplamı {sum((SOL & SAG).values())} | "
      f"küme kesişiminin öğe sayısı {len(set(SOL) & set(SAG))}")
```

```
kap                 d[yok] sonucu   sonra kaç anahtar
sözlük              KeyError        1
varsayılan sözlük   0               2
sayaç               0               1

sol sayaç {'a': 2, 'b': 3, 'c': 2}
sağ sayaç {'a': 1, 'b': 2, 'd': 2}
işlem                 sonuç
sayaç kesişimi        {'a': 1, 'b': 2}
sayaç birleşimi       {'a': 2, 'b': 3, 'c': 2, 'd': 2}
sayaç farkı           {'a': 1, 'b': 1, 'c': 2}
küme kesişimi         ['a', 'b']
küme birleşimi        ['a', 'b', 'c', 'd']

sayaç bir sözlüktür -> True | toplam kayıt 7 | ayrı anahtar 3
kesişimin toplamı 3 | küme kesişiminin öğe sayısı 2
```

Üç kap üç ayrı şey yapıyor. Sözlük istisna veriyor ve kabı değiştirmiyor: anahtar sayısı
**1**'de kalıyor. Varsayılan sözlük **0** veriyor ve anahtar sayısı **2**'ye çıkıyor — yani
yalnız okuma niyetiyle yapılan bir erişim kaba **kalıcı bir girdi eklemiş** oluyor. Sayaç
ise **0** veriyor ve anahtar sayısı **1**'de kalıyor: eksik anahtara sıfır der, ama onu
kaydetmez.

Bu ayrım gruplama ölçümünün doğrudan sonucudur. Eksik anahtarda üreticiyi çağırmak
gruplamada aradığımız davranıştı; aynı davranış yalnız bakmak isteyen bir erişimde
**istenmeyen bir yan etkidir**. Bir varsayılan sözlükte "bu anahtar var mı" diye köşeli
ayraçla sorulmaz; üyelik sınamasıyla ya da `get` ile sorulur.

Alt blok önceki dersin kapanış sorusunu yanıtlıyor. Kümeye verilen veride "kaç kez"
kaybolmuştu. Sayaç aynı hesapları yapıyor ve sayıyı **koruyor**: kesişim her anahtar için
**en küçük** sayıyı, birleşim **en büyük** sayıyı, fark ise çıkarma sonucunu veriyor —
sıfırın altına inen anahtar sonuçta hiç görünmüyor. Küme aynı iki veride yalnız anahtarları
veriyor. Son satır farkı tek bir çiftle özetliyor: küme kesişiminde **2** öğe var, sayaç
kesişiminin toplamı ise **3**. Aynı iki anahtar, iki ayrı bilgi düzeyi.

#### Özet

- Bir kalıbın iki ayrı fiyatı vardır — satır ve nesne — ve bunlar bağımsız değişebilir:
  `setdefault` ile yazım satırı **6**'dan **4**'e indirirken var edilen kabı **100**'den
  **320**'ye çıkarır.
- Varsayılan bir **argüman** olarak yazıldığında her çağrıda var edilir; **üretici** olarak
  verildiğinde yalnız anahtar eksikse çağrılır — varsayılan sözlük aynı **4** satırla
  **100** kap kurar.
- Sayma tarafında kazanç yalnız satırdadır: **7**, **4** ve **1** satırlık üç yazım da aynı
  sonucu verir ve **0** nesne var eder.
- Bin öğelik akışta üç yazım da **1000** nesne var eder; kap yalnız **tutulanı** belirler —
  kırpmasız listede **1000**, elle kırpılan listede ve çift uçlu kuyrukta **5**.
- Döndürme listede **3** ara kap kurup yeni bir kap döndürür, kuyrukta **0** kap kurup aynı
  kabı yerinde çevirir; iki sonuç aynı sıradadır ve öğeler paylaşılır.
- Eksik anahtarda sözlük `KeyError` verir, varsayılan sözlük **0** verip anahtarı **ekler**
  (**1**'den **2**'ye), sayaç **0** verip eklemez; sayaç ayrıca kesişim ve farkı sayıları
  koruyarak hesaplar.

#### Sonraki Adım

Bu dersin döndürme ölçümünde iki dilim alındı ve sonuç yeni bir kap oldu. Dilim burada bir
ayrıntı olarak geçti; oysa kendi başına bu kursun en çok yanlış anlaşılan işlemidir. Sonraki
ders yalnız ona bakar ve tek bir soruyu sonuna kadar götürür: bir dilim alındığında **ne**
kopyalanır? Kabın kendisi mi, içindekiler mi, yoksa ikisi de mi? Yanıt iç içe bir listede
ölçülecek ve sayılar tek başına konuşacak.

### Dilimleme

Source: https://academia.sh/tr/kurslar/python-veri-yapilari/dilimleme

Sekiz dilim biçiminin sekizi de 0 yeni öğe var edip yeni bir kap döndürür; iç içe listede 4 öğe üretilir ve dilim kopyasında iki iç listenin ikisi de paylaşılır — dış kap yenidir, içindekiler eskidir.

Önceki dersin döndürme ölçümünde iki dilim alındı ve sonuç yeni bir kap oldu. Dilim orada
bir ayrıntı olarak geçti; oysa kendi başına bu kursun en çok yanlış anlaşılan işlemidir. Bu
ders tek bir soruyu sonuna kadar götürür: bir dilim alındığında **ne** kopyalanır? Kabın
kendisi mi, içindekiler mi, yoksa ikisi de mi?

#### Dilim Bir Kap İster

**Dilimleme (slicing)** yazımı üç parça alır: başlangıç, bitiş ve adım. Üçü de isteğe
bağlıdır ve verilmediklerinde kabın uçlarına ve bire düşer. Python Temelleri kursunda bu
yazımın bir **kısaltma** olduğu ölçülmüştü; orada hangi yöntemin çağrıldığı sayıldı, burada
o çağrının **kaç kap kurduğu** sayılıyor.

Dizin tek bir yuva ister ve olmayan yuvayı istemek bir kusurdur; dilim bir **aralık** ister
ve aralığın kabın dışına taşan kısmı sessizce kırpılır.

- **KL65** — Sekiz dilim biçimi de **aynı altı öğelik kaynakla** çalışır; öğeler bir kez var
  edilir ve sayaç her dilimden hemen önce sıfırlanır. "Öğeler kaynaktan" sütunu, dilimdeki
  her öğenin kaynakta `is` ile aranmasıyla bulunur.
- **KL66** — Dilime atama ve dilim silme ölçümü **tek bir liste üzerinde sırayla** ilerler;
  ikinci satır birincinin bıraktığı durumdan devam eder.

```python
"""Dilim okumasi: her dilim yeni bir kap kurar, hicbiri yeni oge var etmez."""

SAYAC = {"uretilen": 0}


class 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"]


KAYNAK = [Oge(i) for i in range(6)]

BICIMLER = (("k[1:4]", lambda k: k[1:4]), ("k[:3]", lambda k: k[:3]),
            ("k[3:]", lambda k: k[3:]), ("k[::2]", lambda k: k[::2]),
            ("k[::-1]", lambda k: k[::-1]), ("k[:]", lambda k: k[:]),
            ("k[4:100]", lambda k: k[4:100]), ("k[10:20]", lambda k: k[10:20]))

print(f"{'dilim':<12s}{'yeni öğe':>9s}{'yuva':>6s}{'aynı kap':>10s}"
      f"{'öğeler kaynaktan':>18s}{'içerik':>26s}")
for ad, al in BICIMLER:
    sifirla()
    dilim = al(KAYNAK)
    kaynaktan = all(any(x is y for y in KAYNAK) for x in dilim)
    print(f"{ad:<12s}{uretilen():>9d}{len(dilim):>6d}"
          f"{str(dilim is KAYNAK):>10s}{str(kaynaktan):>18s}"
          f"{str([o.deger for o in dilim]):>26s}")

print()
try:
    KAYNAK[10]
except IndexError as e:
    print(f"sınır dışı dizin: {type(e).__name__} | "
          f"sınır dışı dilim: {[o.deger for o in KAYNAK[10:20]]}")

print()
hedef = list(KAYNAK)
once = hedef
sifirla()
hedef[1:3] = [Oge(9), Oge(9), Oge(9)]
print(f"dilime atama: var edilen {uretilen()} | aynı kap "
      f"{hedef is once} | yuva {len(once)} -> {[o.deger for o in hedef]}")

sifirla()
del hedef[::2]
print(f"dilim silme:  var edilen {uretilen()} | aynı kap "
      f"{hedef is once} | yuva {len(hedef)} -> {[o.deger for o in hedef]}")

print()
metin = "kuzey-2031"
print(f"dizgi dilimi {metin[6:]!r} | demet dilimi "
      f"{tuple(o.deger for o in KAYNAK)[1:4]}")
```

```
dilim        yeni öğe  yuva  aynı kap  öğeler kaynaktan                    içerik
k[1:4]              0     3     False              True                 [1, 2, 3]
k[:3]               0     3     False              True                 [0, 1, 2]
k[3:]               0     3     False              True                 [3, 4, 5]
k[::2]              0     3     False              True                 [0, 2, 4]
k[::-1]             0     6     False              True        [5, 4, 3, 2, 1, 0]
k[:]                0     6     False              True        [0, 1, 2, 3, 4, 5]
k[4:100]            0     2     False              True                    [4, 5]
k[10:20]            0     0     False              True                        []

sınır dışı dizin: IndexError | sınır dışı dilim: []

dilime atama: var edilen 3 | aynı kap True | yuva 7 -> [0, 9, 9, 9, 3, 4, 5]
dilim silme:  var edilen 0 | aynı kap True | yuva 3 -> [9, 9, 4]

dizgi dilimi '2031' | demet dilimi (1, 2, 3)
```

#### Sekiz Dilim, Sıfır Öğe

Tablonun ilk sütunu sekiz satırda da **0**: dilim almak hiçbir öğe var etmez. Üçüncü sütun
sekiz satırda da **False**: her dilim listede **yeni bir kaptır**, tam dilim bile. Dördüncü
sütun sekiz satırda da **True**: dilimdeki her öğe kaynağın öğesidir. Üç sütun birlikte bu
dersin tek cümlelik bulgusunu veriyor — **dilim kabı yeniler, içindekileri değil.** Adım
parçası içeriği seçer ama tabloyu değiştirmez; `k[::2]` üç, `k[::-1]` altı yuva alır ve
ikisi de **0** öğe var eder.

Son iki satır dilimin dizinden ayrıldığı yeri gösteriyor: kabın dışındaki bir **yuva**
istendiğinde `IndexError` doğuyor, kabın dışındaki bir **aralık** istendiğinde `k[4:100]`
iki, `k[10:20]` sıfır öğe veriyor ve hiçbiri kusur saymıyor. Bir aralık istemek belli bir
yuvayı istemekten farklı bir sorudur.

Alt bloktaki üç satır dilimin öteki yüzüdür: dilim **okunduğunda** yeni bir kap doğar, dilime
**yazıldığında** kap yerinde değişir — `hedef is once` **True** kalıyor. Bu satırların ölçüsü
"Yazmak Kopyalamak Değildir" bölümünde açılıyor.

#### Kopya Ne Kadar Derin

Buraya kadar ölçülen kaynak düz bir listeydi. Asıl soru kap içinde kap olduğunda ortaya
çıkar: iç içe bir listede dilim alındığında **dış kap** yeni olur — bu ölçüldü. Peki **iç
kaplar**?

- **KL67** — İç içe kaynak **iki iç liste** ve **dört yaprak öğe** taşır; ölçümün ilk satırı
  bunu doğrular. Değiştirme sınaması ölçümün **sonunda** yapılır; ondan öncesi kaynağa
  dokunulmamış hâliyle alınmıştır.
- **KL68** — Derin kopya nesneleri **kurucudan geçirmez**, bu yüzden üretim sayacı onu
  göremez; yeni nesne kurulup kurulmadığı **kimlikle** sayılır: kopyanın yapraklarından
  kaynakta `is` ile bulunamayanlar.

```python
"""Kopya bir katman derinligindedir: dis kap yenidir, icindekiler eskidir."""

import copy

SAYAC = {"uretilen": 0}


class 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 ic_ice():
    sifirla()
    ic = [[Oge(1), Oge(2)], [Oge(3), Oge(4)]]
    dilim = ic[:]
    liste_kopya = list(ic)
    paylasilan = sum(1 for a, b in zip(ic, dilim) if a is b)
    return uretilen(), paylasilan, dilim[0] is ic[0], liste_kopya[0] is ic[0]


ic_uretim, paylasilan, dilim_ayni, liste_ayni = ic_ice()
print(f"iç içe listede üretilen öğe {ic_uretim}, dilim kopyasında paylaşılan "
      f"iç liste {paylasilan}/2")
print(f"dilim[0] is ic[0] -> {dilim_ayni}, list(ic)[0] is ic[0] -> {liste_ayni}")


def yapraklar(k):
    return [o for ic_liste in k for o in ic_liste]


def ayri_oge(kopya, kaynak):
    """Kopyadaki yapraklardan kaynakta bulunmayanlarin sayisi."""
    eski = yapraklar(kaynak)
    return sum(1 for o in yapraklar(kopya) if not any(o is y for y in eski))


print()
sifirla()
ic = [[Oge(1), Oge(2)], [Oge(3), Oge(4)]]
print(f"kaynak kurulurken var edilen öğe: {uretilen()} | "
      f"iç liste {len(ic)} | yaprak {len(yapraklar(ic))}")

KOPYALAR = (("k[:]", ic[:]), ("list(k)", list(ic)),
            ("copy.copy(k)", copy.copy(ic)),
            ("copy.deepcopy(k)", copy.deepcopy(ic)))

print(f"{'kopya biçimi':<20s}{'ayrı yaprak':>12s}{'dış kap aynı':>13s}"
      f"{'iç liste aynı':>14s}{'yaprak aynı':>12s}")
for ad, k in KOPYALAR:
    print(f"{ad:<20s}{ayri_oge(k, ic):>12d}{str(k is ic):>13s}"
          f"{str(k[0] is ic[0]):>14s}{str(k[0][0] is ic[0][0]):>12s}")

print()
dilim = KOPYALAR[0][1]
derin = KOPYALAR[3][1]
ic[0][0].deger = 99
print(f"kaynaktaki yaprak değiştirildi -> ic[0][0] = {ic[0][0]}")
print(f"  dilim[0][0] = {dilim[0][0]} | derin[0][0] = {derin[0][0]}")
ic.append([Oge(5)])
print(f"kaynağa iç liste eklendi -> ic yuva {len(ic)} | "
      f"dilim yuva {len(dilim)} | derin yuva {len(derin)}")
```

```
iç içe listede üretilen öğe 4, dilim kopyasında paylaşılan iç liste 2/2
dilim[0] is ic[0] -> True, list(ic)[0] is ic[0] -> True

kaynak kurulurken var edilen öğe: 4 | iç liste 2 | yaprak 4
kopya biçimi         ayrı yaprak dış kap aynı iç liste aynı yaprak aynı
k[:]                           0        False          True        True
list(k)                        0        False          True        True
copy.copy(k)                   0        False          True        True
copy.deepcopy(k)               4        False         False       False

kaynaktaki yaprak değiştirildi -> ic[0][0] = Oge(99)
  dilim[0][0] = Oge(99) | derin[0][0] = Oge(1)
kaynağa iç liste eklendi -> ic yuva 3 | dilim yuva 2 | derin yuva 2
```

#### Dış Kap Yenidir, İçindekiler Eskidir

İlk iki satır bu kursun üçüncü iddiasını tam olarak ödüyor. İç içe listede **4** öğe
üretiliyor; dilimle alınan kopyada **iki iç listenin ikisi de paylaşılıyor** —
`dilim[0] is ic[0]` **True** — ve `list()` kopyasında da öyle. **Kopya bir katman
derinliğindedir.**

Alt tablo aynı ölçümü dört kopya biçimine yayıyor ve üçünü aynı yere koyuyor. Bu üçü
**yüzeysel kopyadır (shallow copy)**: dilim, liste kurucusu ve kopyalama işlevi. Üçünde de
dış kap **ayrı**, iç liste **aynı**, yaprak **aynı** ve ayrı yaprak sayısı **0**. Dördüncüsü
ayrılıyor: derin kopyada üç sütun da ayrı ve ayrı yaprak sayısı **4**. Bu **0**'a karşılık
**4**, iki yazım arasındaki bütün farkı verir.

Son blok farkın anlamını gösteriyor. Kaynaktaki bir yaprağın içi değiştirildiğinde dilim
kopyası bunu **görüyor** — `dilim[0][0]` de `Oge(99)` oldu — derin kopya **görmüyor**.
Kaynağa yeni bir iç liste eklendiğinde ise ikisi de **2** yuvada kalıyor, kaynak **3**'e
çıkıyor. Paylaşılan şey **dış kabın yuvalarının gösterdiği nesnelerdir**, kabın kendisi
değil; yüzeysel kopya bu yüzden tam olarak **bir katman** bağımsızdır.

#### Yazmak Kopyalamak Değildir

Önceki bölüm dilimi **okudu** ve bir katman kopyaladı. Dilime **yazıldığında** ölçü tersine
döner: kap kurulmaz, kimlik korunur, uzunluk değişir. Bu bölüm o karşıtı ve dersin
iddiasının sınırını ölçer.

- **KL66 (devam)** — Yazma ölçümü **tek bir liste üzerinde sırayla** ilerler. Silinen öğenin
  yaşayıp yaşamadığı silmeden **önce** alınmış ayrı bir adla sınanır; değiştirilemez kap
  sınaması **dokunulmamış** üç ayrı kapta yapılır.

```python
"""Dilime yazmak kopya degil degisikliktir; degismez kapta tam dilim yeni kap kurmaz."""

SAYAC = {"uretilen": 0}


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


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


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


liste = [Oge(i) for i in range(6)]
kimlik = liste

for ad, d in (("k[4:1:-1]", liste[4:1:-1]), ("k[1:4:-1]", liste[1:4:-1]),
              ("k[::-2]", liste[::-2])):
    print(f"{ad:<10s} yuva {len(d)} -> {[o.deger for o in d]}")

print()
sifirla()
liste[1:4] = [Oge(7)]
print(f"k[1:4] = tek öğe: var edilen {uretilen()} | aynı kap {liste is kimlik} | "
      f"yuva 6 -> {len(liste)} | {[o.deger for o in liste]}")

ikinci = liste[1]
tutulan = liste[1:3]
sifirla()
del liste[1:3]
print(f"del k[1:3]:      var edilen {uretilen()} | aynı kap {liste is kimlik} | "
      f"yuva {len(liste)} | {[o.deger for o in liste]}")
print(f"  silinen öğe ayrı adda yaşıyor -> {ikinci is tutulan[0]} | "
      f"kapta kaldı -> {any(ikinci is x for x in liste)}")

print()
for ad, kap in (("liste", [1, 2, 3]), ("demet", (1, 2, 3)), ("dizgi", "abc")):
    print(f"{ad:<7s} k[:] is k -> {str(kap[:] is kap):<6s} k[:2] is k -> {kap[:2] is kap}")
```

```
k[4:1:-1]  yuva 3 -> [4, 3, 2]
k[1:4:-1]  yuva 0 -> []
k[::-2]    yuva 3 -> [5, 3, 1]

k[1:4] = tek öğe: var edilen 1 | aynı kap True | yuva 6 -> 4 | [0, 7, 4, 5]
del k[1:3]:      var edilen 0 | aynı kap True | yuva 2 | [0, 5]
  silinen öğe ayrı adda yaşıyor -> True | kapta kaldı -> False

liste   k[:] is k -> False  k[:2] is k -> False
demet   k[:] is k -> True   k[:2] is k -> False
dizgi   k[:] is k -> True   k[:2] is k -> False
```

Üst üç satır eksi adımın ölçüsü. `k[::-1]` ana tabloda zaten **0** öğe var edip yeni bir kap
vermişti; buradaki soru boş dilimin eksi adımdaki karşılığı. `k[4:1:-1]` üç yuva verirken
`k[1:4:-1]` **0** yuva veriyor: sınırlar aynı, yön ters — geriye giden dilimde başlangıcın
bitişten büyük olması gerekir.

Orta blok yazmanın ölçüsü. `k[1:4] = tek öğe` **1** nesne var ediyor — onu biz kurduk — ve
`liste is kimlik` **True** kalıyor: kap değişmedi, kabın içi değişti. Yuva **6**'dan **4**'e
**düşüyor**; üç yuva alınıp bir yuva kondu, yani atama alınan ve konan yuva sayısının
eşitliğini istemez. `del k[1:3]` aynı ekseni tamamlıyor: **0** öğe var ediliyor, kimlik yine
korunuyor, yuva **2**'ye iniyor. Listede `k[:]` yeni bir kap **döndürür**; atama ve silme
var olan kabı **yerinde değiştirir**. Silinen öğe de yok olmuyor: silmeden önce alınan adla
hâlâ aynı nesne (**True**), artık kapta değil (**False**) — silme **yuvayı** kaldırır.

Alt blok iddianın sınırı. Listede `k[:] is k` **False**, demette ve dizgide **True**: tam
dilim orada yeni bir kap kurmuyor, var olanı geri veriyor. **Dış kap yenidir cümlesi
değiştirilemez tiplerde geçerli değildir**, çünkü içeriği değiştirilemeyen bir kabı
çoğaltmanın ölçülebilir bir karşılığı yoktur. `k[:2] is k` üç kapta da **False**; sınır tam
dilime özgü. İddia daralıyor — **dilim, değiştirilebilir bir kabı yeniler; içindekileri
hiçbir zaman yenilemez.**

#### Kırpmayı Kim Yapıyor

Üç ölçüm de dilimin ne yaptığını gösterdi; hiçbiri **kimin** yaptığını göstermedi. Sınır
dışı aralığın kırpılması bir kap davranışı gibi göründü, oysa değildir: iki nokta üst üste
yazımının arkasında adı belli bir nesne vardır.

- **KL69** — Tek bir dilim tarifi nesnesi kurulur ve **üç ayrı kapta** kullanılır; sonuçlar
  aynı tarifin yazımla alınan karşılığıyla karşılaştırılır.
- **KL70** — Çözülen sınırlar tarifin **kendi yönteminden** okunur, elle hesaplanmaz; yuva
  sayısı ayrıca dilimin uzunluğundan alınır ve iki kaynak birbirini doğrular.

```python
"""Dilim tarifi bir nesnedir: ayni tarif uc kapta kullanilir, sinirlar ondan cozulur."""

KAYNAK = list(range(6))
KAPLAR = (("liste", KAYNAK), ("demet", tuple(range(6))), ("dizgi", "kuzeyi"))

TARIF = slice(1, 4)
print(f"tarif bir nesnedir: {type(TARIF).__name__} | "
      f"başlangıç {TARIF.start} bitiş {TARIF.stop} adım {TARIF.step}")
print(f"{'kap':<10s}{'k[tarif]':<14s}{'k[1:4] ile aynı'}")
for ad, kap in KAPLAR:
    print(f"{ad:<10s}{str(kap[TARIF]):<14s}{kap[TARIF] == kap[1:4]}")
print(f"tek tarif nesnesi {len(KAPLAR)} ayrı kapta kullanıldı")

print()
print(f"{'yazım':<12s}{'çözülen sınırlar':<20s}{'yuva'}")
for ad, t in (("k[1:4]", slice(1, 4)), ("k[:]", slice(None, None)),
              ("k[::-1]", slice(None, None, -1)), ("k[-3:]", slice(-3, None)),
              ("k[4:100]", slice(4, 100)), ("k[10:20]", slice(10, 20))):
    print(f"{ad:<12s}{str(t.indices(len(KAYNAK))):<20s}{len(KAYNAK[t])}")
```

```
tarif bir nesnedir: slice | başlangıç 1 bitiş 4 adım None
kap       k[tarif]      k[1:4] ile aynı
liste     [1, 2, 3]     True
demet     (1, 2, 3)     True
dizgi     uze           True
tek tarif nesnesi 3 ayrı kapta kullanıldı

yazım       çözülen sınırlar    yuva
k[1:4]      (1, 4, 1)           3
k[:]        (0, 6, 1)           6
k[::-1]     (5, -1, -1)         6
k[-3:]      (3, 6, 1)           3
k[4:100]    (4, 6, 1)           2
k[10:20]    (6, 6, 1)           0
```

Üst blok yazımın arkasındaki nesneyi çıkarıyor. `k[1:4]` yazıldığında kaba geçirilen şey iki
sayı değil, **tek bir tariftir**; üç alanı vardır ve verilmeyen alan `None` kalır. Aynı tarif
üç ayrı kapta kullanılıyor ve üçünde de yazımla alınan sonucun aynısını veriyor. Tarif kaba
bağlı değildir: hangi kaba uygulanacağını da, kabın uzunluğunu da bilmez.

Alt blok kırpmanın nerede olduğunu gösteriyor. Tarif, uygulanacağı kabın uzunluğunu
öğrendiğinde kendini **somut üç sayıya** çözüyor: `k[:]` `(0, 6, 1)` oluyor ve verilmeyen
alanlar doluyor; `k[-3:]` `(3, 6, 1)` oluyor, eksi sayı uzunluktan çıkarılıyor; `k[4:100]`
`(4, 6, 1)` oluyor, **100** sayısı **6**'ya kırpılıyor; `k[10:20]` `(6, 6, 1)` oluyor ve
dilim boşalıyor.

Sınır dışı aralığın kusur saymaması bir davranış tercihi değil, ölçülmüş bir adımdır —
**kırpma, kabın içine girilmeden önce tarifin çözümünde yapılır.** Ters adımlı tarifin
`(5, -1, -1)` vermesi de aynı çözümün ürünüdür ve `k[1:4:-1]`'in boşluğunu o açıklar.

#### Özet

- Sekiz dilim biçiminin sekizi de **0** öğe var eder, sekizi de listede **yeni bir kap**
  döndürür ve sekizinde de öğeler kaynaktan gelir — tam dilim dahil.
- Sınır dışı **dizin** `IndexError` verir, sınır dışı **dilim** kırpılır, `k[1:4:-1]` **0**
  yuva verir. Kırpma kabın içine girilmeden, kaba bağlı olmayan **tarif nesnesinin**
  çözümünde yapılır: `k[10:20]` → `(6, 6, 1)`.
- Dilime yazmak **aynı kapta** çalışır ve uzunluğu değiştirir: `k[1:4] = tek öğe` **1** nesne
  var edip yuvayı **6**'dan **4**'e, `del k[1:3]` **0** nesne var edip **2**'ye indirir;
  ikisinde de kimlik korunur ve silinen öğe, başka bir ad tutuyorsa yaşar.
- İç içe listede **4** öğe üretilir; dilim kopyasında **iki iç listenin ikisi de paylaşılır**
  (`dilim[0] is ic[0]` → True) ve `list()` kopyasında da öyle — **dış kap yenidir,
  içindekiler eskidir.** Yüzeysel kopyanın üç yazımı **0** ayrı yaprak verir, derin kopya
  **4**.
- İddianın sınırı: `k[:] is k` listede **False**, demette ve dizgide **True**. Değiştirilemez
  bir kapta tam dilim yeni kap kurmaz — dilim **değiştirilebilir** bir kabı yeniler,
  içindekileri hiçbir zaman yenilemez.

#### Sonraki Adım

Bu dersin bütün kopyaları bir katman derinliğindeydi ve o bir katmanı kurmak bile **her
yuvaya dokunmayı** gerektirdi: altı öğelik bir kaptan dilim almak altı yuvayı okuyup yeni
kaba yazmak demektir, bin öğelik kapta bin yuva. Var edilen öğe **0** olsa bile dokunulan
yuva sayısı kabın uzunluğu kadardır.

Peki öğelerin üzerinden geçmek için kabı yeniden kurmak **zorunlu mu**? Sonraki ders bu
soruyu tersinden sorar: bir kabın öğelerine, ne yeni bir kap kurup ne de eldekine dokunup
sırayla ulaşmanın yolu var mı? Python Temelleri kursunda protokolün **çağıran** tarafı
ölçülmüştü; sonraki ders **kuran** tarafa geçer ve ilk işi şudur: `iter` ile `next`
arasındaki sözleşmeyi kendi elimizle yazmak.

## Yineleme ve Fonksiyonel Araçlar

Yineleyici sözleşmesi, tembel değerlendirme, bellek dostu dönüşüm zincirleri, üç kavrama biçimi, fonksiyonu argüman olarak geçme ve davranışı sarmalarken meta veriyi koruma.

### Yineleyici Protokolü

Source: https://academia.sh/tr/kurslar/python-veri-yapilari/yineleyici-protokolu

Öğ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?

### Üreteçler

Source: https://academia.sh/tr/kurslar/python-veri-yapilari/uretecler

Üreteç işlevini çağırmak gövdesinden tek satır çalıştırmaz: bin öğelik iş kavramada kurulumda 1000 nesne var ederken üreteçte 0 var eder ve üç öğe istendiğinde 3'te kalır.

Önceki ders yineleyici sözleşmesini ölçtü ve onu elle yazan bir sınıf kurdu: `Sayim` beş
satırda iki özel yöntem, bir sınır ve bir konum alanı istedi. Hepsi tek bir iş içindi —
nerede kalındığını hatırlamak. O iş her yineleyicide tekrarlanır ve konum tek bir sayı
olmadığında sınıf hızla büyür. İç içe bir gezinti için birden çok sayaç, dallanmış bir
arama için bir yığıt gerekir; her biri elle taşınmalıdır.

Oysa sıradan bir işlev bu bilgiyi zaten taşır. Bir işlev çalışırken hangi satırda olduğu,
yerel adlarının hangi değerlere bağlı olduğu bellidir; yalnız işlev bittiğinde hepsi
kaybolur. Bu dersin sorusu şudur: işlev **bitmek yerine duraklayabilseydi**, konumu ayrıca
yazmaya gerek kalır mıydı? Python'un yanıtı **üreteç (generator)** işlevidir ve ölçülecek
sayı şudur — böyle bir işlevi çağırmak, gövdesindeki bin üretimden kaçını çağrı anında
yapar?

#### Duraklayan İşlev

Bir işlev gövdesinde `return` yerine `yield` geçiyorsa o işlev artık sıradan bir işlev
değildir. Çağrıldığında gövdesi çalışmaz; geriye bir **üreteç nesnesi** döner. Gövde ancak
o nesneden bir öğe istendiğinde çalışmaya başlar ve ilk `yield` deyimine gelince **durur**.
Durduğu yerde yerel adlar, döngü sayaçları ve çağrı çerçevesi olduğu gibi kalır. Bir sonraki
istek geldiğinde yürütme `yield`'in hemen ardından devam eder.

Ayrım burada nettir: `return` çerçeveyi yıkar, `yield` **askıya alır**. Programlama
Temelleri kursu tembel değerlendirmeyi bir paradigma tercihi olarak tanıtmıştı — değerin
kullanıldığı ana kadar hesaplanmaması. Burada ölçülen şey o tercihin adı değil, Python'un
onu hangi düzenekle verdiği ve **kaç nesneye mal olduğudur**.

#### Üreteç Bir Yineleyicidir

Üreteç nesnesi önceki dersin sözleşmesini imzalar: `__iter__` ve `__next__` tanımlıdır,
`__iter__` kendisini döndürür, gövde bittiğinde `StopIteration` fırlatılır. Yani üreteç,
`Sayim` sınıfının elle yazdığı şeyin dil tarafından yazılmış hâlidir — sözleşmenin **yazan
tarafı**, ama sınıf kurmadan.

Buradan iki sonuç doğrudan gelir. Üreteç **tek geçişliktir**, çünkü `__iter__` kendisini
döndürür. Ve tükendikten sonra yenilenmez, çünkü gövdesi sona ulaşmıştır. İkisi de önceki
derste sözleşmenin sonucu olarak kurulmuştu; burada bir kez daha ölçülür.

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

- **YN10** — Ortak tanımın `Oge` sınıfı ve `N = 1000` sayısı aynen kullanılır; sayma düzeni
  değiştirilmez.
- **YN11** — Üç biçim de **aynı** işi tanımlar: bin öğelik bir akış. Değişen tek şey
  yazımdır; kâhin kurgunun kendisidir, çünkü üretimi nesne kendi sayar.
- **YN12** — "Kurulumda" ölçümü, biçimin yazıldığı satır çalıştıktan hemen sonra alınır.
  "Üç öğe sonra" ölçümü, üç öğe istendikten sonra alınır; kavramada bu istek `liste[:3]`
  ile yapılır, öbür ikisinde `next` ile.
- **YN13** — İz kaydı gövdenin içine yerleştirilmiş bir listedir; her satır hangi noktaya
  ulaşıldığını yazar. "Gövde çalıştı mı" sorusunun ölçüsü bu listedir.
- **YN14** — İz ölçümünde öğe sayısı üçtür; ölçülen şey sayı değil, çağrı ile gövdenin
  ilerlemesi arasındaki ilişkidir.
- **YN15** — Sonsuz üreteç ölçümü kasıtlıdır: kurulumun sıfır üretmesi, öğe sayısının sonlu
  olmasından gelmiyor.
- **YN16** — Birleştirme ölçümünde iki kanat **400** ve **600** öğelidir; toplam yine
  **1000**'dir ve üç biçim aynı öğe dizisini verir.
- **YN17** — "İlk öğeye dek" sütunu, birleşimden yalnız bir öğe alındıktan sonraki üretim
  sayısıdır; biçimler arasındaki farkı bu sütun ayırır.
- **YN18** — Kapanış ölçümünde akış bin öğeliktir ama üç öğe alındıktan sonra `close` ile
  bırakılır; `finally` bloğuna konan iz satırı o anki üretim sayısını da yazar, böylece
  kapanışın ne zaman çalıştığı ölçülebilir.

#### Ölçüm

```python
"""Uretec: govde ne zaman calisir, kurulum kac nesne var eder."""

SAYAC = {"uretilen": 0}
IZ = []


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"]


N = 1000


def uretec_islevi(n=N):
    for i in range(n):
        yield Oge(i)


def kavrama(n=N):
    sifirla()
    liste = [Oge(i) for i in range(n)]
    kurulumda = uretilen()
    ilk_uc = liste[:3]
    return kurulumda, uretilen(), len(ilk_uc)


def uretec_ifadesi(n=N):
    sifirla()
    akis = (Oge(i) for i in range(n))
    kurulumda = uretilen()
    ilk_uc = [next(akis) for _ in range(3)]
    return kurulumda, uretilen(), len(ilk_uc)


def uretec_cagrisi(n=N):
    sifirla()
    akis = uretec_islevi(n)
    kurulumda = uretilen()
    ilk_uc = [next(akis) for _ in range(3)]
    return kurulumda, uretilen(), len(ilk_uc)


print(f"{'biçim':<24s} {'kurulumda':>10s} {'üç öğe sonra':>13s} {'elde':>5s}")
for ad, f in (("kavrama", kavrama), ("üreteç ifadesi", uretec_ifadesi),
              ("üreteç işlevi çağrısı", uretec_cagrisi)):
    a, b, c = f()
    print(f"  {ad:<22s} {a:10d} {b:13d} {c:5d}")


def izli(n=3):
    IZ.append("gövde başladı")
    for i in range(n):
        IZ.append(f"yield {i} öncesi")
        yield Oge(i)
        IZ.append(f"yield {i} sonrası")
    IZ.append("gövde bitti")
    return "son"


IZ.clear()
g = izli()
print()
print(f"çağrıdan sonra iz: {IZ}")
next(g)
print(f"birinci next sonrası: {IZ}")
next(g)
print(f"ikinci next sonrası: {IZ}")
print(f"üreteç yineleyici mi: iter(g) is g -> {iter(g) is g}")

IZ.clear()
son = izli(1)
next(son)
try:
    next(son)
except StopIteration as e:
    print(f"gövde bittiğinde: {IZ[-1]!r}, istisnanın taşıdığı değer {e.value!r}")

tek = uretec_islevi(3)
print()
print(f"üreteç işlevi tek geçişlik: {len(list(tek))} ve {len(list(tek))} öğe")


def sonsuz():
    i = 0
    while True:
        yield Oge(i)
        i += 1


sifirla()
akis = sonsuz()
kurulum = uretilen()
[next(akis) for _ in range(3)]
print(f"sonsuz üreteç: kurulumda {kurulum}, üç öğe istendiğinde {uretilen()}")


def kanat(a, b):
    yield from uretec_islevi(a)
    yield from uretec_islevi(b)


def elle_kanat(a, b):
    for o in uretec_islevi(a):
        yield o
    for o in uretec_islevi(b):
        yield o


def ara_liste(a, b):
    return list(uretec_islevi(a)) + list(uretec_islevi(b))


print()
print(f"{'birleştirme biçimi':<22s} {'kurulumda':>10s} {'ilk öğeye dek':>14s}"
      f" {'tümü gezilince':>15s}")
for ad, f in (("yield from", kanat), ("iç içe for + yield", elle_kanat),
              ("iki listeyi ekleme", ara_liste)):
    sifirla()
    n = f(400, 600)
    kurulumda = uretilen()
    y = iter(n)
    next(y)
    ilk = uretilen()
    list(y)
    print(f"  {ad:<20s} {kurulumda:10d} {ilk:14d} {uretilen():15d}")


def toplayici():
    toplam = 0
    try:
        while True:
            gelen = yield toplam
            toplam += gelen
    finally:
        IZ.append("toplayıcı kapandı")


def kaynak(n=N):
    try:
        for i in range(n):
            yield Oge(i)
    finally:
        IZ.append(f"kaynak kapandı, o ana dek üretilen {uretilen()}")


IZ.clear()
t = toplayici()
ilk = next(t)
print()
print(f"yield bir ifadedir: ilk değer {ilk}, send(5) -> {t.send(5)},"
      f" send(7) -> {t.send(7)}")
t.close()
print(f"close çağrısından sonra iz: {IZ}")

IZ.clear()
sifirla()
akis = kaynak()
uc = [next(akis) for _ in range(3)]
akis.close()
print(f"bin öğelik akıştan üç öğe alınıp bırakıldı: elde {len(uc)},"
      f" üretilen {uretilen()}")
print(f"kapanış izi: {IZ}")
```

```
biçim                     kurulumda  üç öğe sonra  elde
  kavrama                      1000          1000     3
  üreteç ifadesi                  0             3     3
  üreteç işlevi çağrısı           0             3     3

çağrıdan sonra iz: []
birinci next sonrası: ['gövde başladı', 'yield 0 öncesi']
ikinci next sonrası: ['gövde başladı', 'yield 0 öncesi', 'yield 0 sonrası', 'yield 1 öncesi']
üreteç yineleyici mi: iter(g) is g -> True
gövde bittiğinde: 'gövde bitti', istisnanın taşıdığı değer 'son'

üreteç işlevi tek geçişlik: 3 ve 0 öğe
sonsuz üreteç: kurulumda 0, üç öğe istendiğinde 3

birleştirme biçimi      kurulumda  ilk öğeye dek  tümü gezilince
  yield from                    0              1            1000
  iç içe for + yield            0              1            1000
  iki listeyi ekleme         1000           1000            1000

yield bir ifadedir: ilk değer 0, send(5) -> 5, send(7) -> 12
close çağrısından sonra iz: ['toplayıcı kapandı']
bin öğelik akıştan üç öğe alınıp bırakıldı: elde 3, üretilen 3
kapanış izi: ['kaynak kapandı, o ana dek üretilen 3']
```

#### Kurulumun Bedeli

Üst tablo ortak tanımın ikinci iddiasının ilk yarısıdır ve üç satır aynı işi tanımlar: bin
öğelik bir akış.

**Kavrama** daha yazıldığı satırda **1000** nesne var ediyor. Üç öğe istendiğinde sayı
değişmiyor — çünkü zaten hepsi üretilmişti; `liste[:3]` yalnız var olanlardan üçünü
gösteriyor.

**Üreteç ifadesi** ve **üreteç işlevi çağrısı** kurulumda **0** var ediyor. Üç öğe
istendiğinde **3**'e çıkıyor. Aradaki 997 nesne hiç doğmuyor, çünkü kimse istemedi.

İki sayı arasındaki uçurum yazımda görünmez. Üç satırın üçü de tek bir deyimdir, üçü de bin
öğelik bir akışı adlandırır, üçü de sonrasında `for` döngüsüne sokulabilir. Kurulumun
**1000** mi **0** mı olduğunu söyleyen tek şey köşeli ayraç ile parantez arasındaki fark ve
`yield` anahtar sözcüğüdür.

Buradaki tembelliğin sınırını doğru okumak gerekir. **0**, işin yapılmadığı anlamına gelmez;
**henüz** yapılmadığı anlamına gelir. Üç öğe istendiğinde üç nesne doğuyor; bin öğe
istenirse bin nesne doğacak. Bu dersin ölçtüğü sayı yalnız **ne zaman** sorusunun yanıtıdır;
**toplamda kaç** sorusunun yanıtı bir sonraki dersin konusudur.

`üreteç ifadesi` satırının yazımı — köşeli ayraç yerine parantez — bu dersin konusu
değildir; sonraki ders onu kendi başına ölçer. Burada yalnız kurulum sayısını vermek için
tabloda duruyor.

#### Gövde Nerede Duruyor

Orta blok gövdenin ilerleyişini iz kaydıyla gösteriyor ve ilk satır en şaşırtıcı olanıdır:
üreteç işlevi çağrıldıktan sonra iz **boş**. Gövdeden tek satır çalışmamış. Çağrı bir işi
başlatmamış, bir **nesne kurmuş**.

Birinci `next` çağrısından sonra iz iki satır: gövde başladı ve ilk `yield`'e kadar geldi.
Orada durdu. İkinci çağrıdan sonra iki satır daha eklendi — yürütme `yield`'in **ardından**
devam etti, döngünün bir sonraki turuna geçti ve ikinci `yield`'de yine durdu. Yerel `i`
adı iki çağrı arasında korundu; kimse onu bir yere yazmadı, çerçeve olduğu gibi askıda
bekledi.

Bu, `Sayim` sınıfının elle yaptığı işin tam karşılığıdır. Orada konum bir öznitelikti ve her
çağrıda elle ilerletiliyordu; burada konum **gövdenin kendisidir** ve dil onu koruyor.
`iter(g) is g` satırı sözleşmenin öbür ucunu doğruluyor: üreteç kendi yineleyicisidir. Tek
geçişlilik de aynı yerden geliyor — `3` ve `0`.

#### Bitişin Taşıdığı Değer

Gövde sona ulaştığında iz son satırını yazıyor ve `StopIteration` doğuyor. İlginç olan
istisnanın **boş olmaması**: `return "son"` deyiminin değeri istisnanın üzerinde taşınıyor.

Bu, `return` ile `yield`'in bir üreteçte nasıl bölüştüğünü gösterir. `yield` akışın
öğelerini verir; `return` akışın **sonucunu** verir ve akışın parçası değildir. Bir üreteci
`for` ile gezen taraf bu değeri hiç görmez, çünkü döngü `StopIteration`'ı yakalayıp atar.
Değeri isteyen tarafın istisnayı kendisi yakalaması gerekir.

Sonsuz üreteç satırı tembelliğin sonluluğa bağlı olmadığını gösteriyor: gövdesi hiç bitmeyen
bir döngü olan üreteç kurulumda yine **0** var ediyor ve üç öğe istendiğinde **3**'te
kalıyor. Böyle bir akışı kavramayla yazmak olanaksızdır — kavrama bitmeyen bir kaynağı
tüketmeye çalışır ve hiçbir zaman ilk satırdan çıkmaz. Tembellik burada bir eniyileme değil,
**ifade edilebilirlik** koşuludur.

#### Yetki Devri

Son tablo iki akışı birleştirmenin üç yolunu ölçüyor ve üçü de aynı **1000** öğeyi veriyor.

`yield from` bir üreteci başka bir üretecin öğelerini teslim etmekle görevlendirir: dış
üreteç kendi gövdesini askıya alır ve istekleri iç üretece devreder. Kurulumda **0**, ilk
öğe alındığında **1**. `for` döngüsüyle elle yazılan biçim aynı sayıları veriyor;
`yield from` bu döngünün kısaltmasıdır ve iç üretecin bitiş değerini de aktarır.

**İki listeyi ekleme** satırı ayrılıyor. Kurulumda **1000** — çünkü `list()` çağrıları iki
kanadı da sonuna kadar tüketiyor. İlk öğeye ulaşmadan önce bin nesne doğmuş ve iki ara liste
kurulmuş oluyor; üstelik `+` işleci üçüncü bir liste daha üretir. Sonuç aynı, sıra aynı,
öğeler aynı; ayrılan tek şey ilk öğenin **kaç üretimden sonra** geldiğidir.

Ölçüm bir zincirin nerede kırıldığını da söyler: tembel bir akışa tek bir `list()` çağrısı
girdiğinde zincirin o noktasında tembellik biter. Sonraki halkalar tembel yazılmış olsa bile
kendilerinden önceki bin üretimi geri alamaz.

#### Kanalın İki Yönü ve Kapanış

Son dört satır `yield`'in tek yönlü bir çıkış olmadığını gösteriyor. `yield` bir deyim
değil bir **ifadedir** ve bir değere sahiptir: askıdaki üreteç `send` ile uyandırıldığında,
duran `yield` ifadesi gönderilen değeri döndürür ve gövde oradan devam eder. Ölçümdeki
toplayıcı `send(5)` ile **5**, `send(7)` ile **12** veriyor; toplam iki çağrı arasında
gövdenin yerel adında duruyor.

İlk `next` çağrısı bu düzende zorunludur: gövde henüz ilk `yield`'e ulaşmamışsa gönderilen
değeri alacak bir ifade yoktur. Bu yüzden `send` ile beslenen her üreteç önce bir kez
ilerletilir; ölçümdeki **0** o ilk `yield`'in verdiği başlangıç değeridir.

Son iki satır tembel akışların en kolay atlanan yanını ölçüyor. Bin öğelik akıştan üç öğe
alınıp akış bırakıldığında üretilen **3**'te kalıyor — buraya kadar beklenen. Beklenmedik
olan `finally` bloğunun **çalışmasıdır**: `close` çağrısı askıdaki gövdeyi durduğu noktadan
uyandırır, orada bir istisna doğurur ve `finally` bloklarını yürütür. İz satırı bunu
kanıtlıyor, üstelik o anki üretim sayısını da yazıyor.

Bunun pratikteki karşılığı şudur: bir üreteç açık bir kaynak üzerinde çalışıyorsa, akışın
yarıda bırakılması o kaynağı asılı bırakmaz. Python Temelleri kursunun bağlam yöneticileri
dersi serbest bırakmanın istisna durumunda da yapıldığını kurmuştu; üreteç gövdesindeki
`try`/`finally` aynı güvenceyi **askıya alınmış** bir çerçeve için verir. Tembel olan işi
erteler, sorumluluğu ertelemez.

#### Özet

- `yield` içeren bir işlev çağrıldığında gövdesinden tek satır çalışmaz; geriye bir üreteç
  nesnesi döner. `return` çerçeveyi yıkar, `yield` askıya alır.
- Bin öğelik akışta kavrama kurulumda **1000** nesne var eder, üreteç ifadesi ve üreteç
  işlevi çağrısı **0**; üç öğe istendiğinde ikisi **3**'te kalır. Yazımlar tek deyimlik ve
  benzerdir, sayılar değildir.
- Üreteç önceki dersin sözleşmesini imzalar: `iter(g) is g` doğrudur, akış tek geçişliktir
  ve ikinci geçiş **0** öğe verir. Elle yazılan yineleyici sınıfının yaptığı işi dil yapar.
- Gövde her `yield`'de durur ve bir sonraki istekle o noktadan devam eder; yerel adlar
  arada korunur. Sona ulaşıldığında `return` değeri `StopIteration` üzerinde taşınır ve
  `for` ile gezen taraf onu görmez.
- `yield from` ara kap kurmadan yetki devreder: ilk öğeye dek **1** üretim. Aynı işi iki
  listeyi ekleyerek yapmak ilk öğeden önce **1000** üretim ve üç kap gerektirir.
- `yield` bir ifadedir ve `send` ile gönderilen değeri döndürür; bin öğelik akış üç öğeden
  sonra `close` ile bırakıldığında üretim **3**'te kalır ama gövdedeki `finally` bloğu yine
  çalışır.

#### Sonraki Adım

Bu ders yalnız kurulum anını ölçtü ve orada uçurum vardı: **1000**'e karşı **0**. Ama akış
er ya da geç tüketilir — bin öğe gerçekten toplanacaksa üreteç de o bin nesneyi üretmek
zorunda. Sonraki ders tam bu soruyu sorar: aynı işi sonuna kadar götüren iki yazım, işin
sonunda kaç nesne üretmiş olur ve kaçını hâlâ elinde tutar? Yanıtın ilk yarısı beklenen,
ikinci yarısı beklenmedik çıkacak — ve tembelliğin gerçekte neyi azalttığı ancak orada
görünecek.

### Üreteç İfadeleri

Source: https://academia.sh/tr/kurslar/python-veri-yapilari/uretec-ifadeleri

Bin öğelik toplamada üreteç de kavrama da 1000 nesne üretir ve ikisi de 499500 verir; ayrıldıkları tek yer listede tutulandır — 0'a karşı 1000. Tembel olan üretimi geciktirir, ondan kaçınmaz.

Önceki ders yalnız kurulum anını ölçtü ve orada uçurum vardı: kavrama daha ilk satırda
**1000** nesne var ediyor, üreteç **0** var ediyordu. Ama bir akış kurulup bırakılmak için
yazılmaz; er ya da geç tüketilir. Bin öğe gerçekten toplanacaksa üreteç de o bin nesneyi
üretmek zorundadır.

Bu dersin sorusu budur: aynı işi sonuna kadar götüren iki yazım, iş bittiğinde kaç nesne
üretmiş olur ve kaçını hâlâ elinde tutar? Yanıtın bir yarısı beklenen, öbür yarısı
beklenmedik çıkacak — ve tembelliğin gerçekte neyi azalttığı ancak orada görünecek. Ders
aynı zamanda üreteç ifadesinin kendi yazımını kurar: önceki derste tabloda bir satır olarak
duran biçim burada kendi başına ölçülür.

#### Yazım

Üreteç ifadesi bir liste kavramasının köşeli ayraçlarının parantezle değiştirilmiş hâlidir
ve ürettiği şey bir liste değil, bir **üreteç nesnesidir**. Bir işleve tek argüman olarak
verildiğinde parantezler ayrıca yazılmaz; `sum(o.deger for o in akis)` yazımındaki ifade
kendi başına bir üreteç ifadesidir.

Ürettiği nesne önceki dersin üreteç işlevinin döndürdüğüyle aynı türdendir: yineleyicidir,
tek geçişliktir, `__iter__` kendisini döndürür. Fark yalnız yazımdadır. Üreteç işlevi ad
alır, birden çok deyim taşıyabilir, dallanabilir; üreteç ifadesi adsızdır ve tek bir
dönüşümü anlatır. Aralarındaki seçim maliyet değil, ifade genişliği seçimidir — ölçülen
sayılar ikisinde de aynıdır.

Programlama Temelleri kursu tembel değerlendirmeyi fonksiyonel programlamaya girişte bir
kavram olarak kurmuştu: değer, kullanıldığı ana kadar hesaplanmaz. Bu ders o kavramı
tekrarlamaz; onun Python'daki karşılığının **kaç nesneye mal olduğunu** sayar. Ve sayı,
kavramın adından beklenenden farklı çıkar.

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

- **YN19** — Ortak tanımın `Oge` sınıfı ve `N = 1000` sayısı aynen kullanılır; sayma düzeni
  değiştirilmez. Kâhin kurgunun kendisidir.
- **YN20** — İki tüketim biçimi **aynı** sonucu hedefler: bin öğenin değer toplamı. Sonuç
  sütunu ölçümün doğruluk denetimidir; iki biçim ayrı sonuç verseydi karşılaştırma
  geçersiz olurdu.
- **YN21** — "Listede tutulan", toplama bittiğinde bir kapta canlı duran öğe sayısıdır;
  üreteçli biçimde böyle bir kap kurulmadığı için sıfırdır.
- **YN22** — Zincir ölçümünde üç halka vardır: üretme, süzme, dönüştürme. İki biçim aynı
  halkaları aynı sırada uygular; değişen tek şey her halkanın kap mı akış mı olduğudur.
- **YN23** — Zincirdeki "kaplarda tutulan" sayısı üç listenin uzunluklarının toplamıdır.
  **Bellek iddiası sayılan nesneyle kurulur, ölçülen baytla değil; süre ölçülmez.**
- **YN24** — Erken bırakma ölçümünde aranan koşul altıncı öğede sağlanır; koşulun yeri
  sabittir ve tüketicinin ne zaman durduğunu belirler.
- **YN25** — Tek geçişlilik ölçümü on öğelik küçük bir akışta yapılır; ölçülen şey büyüklük
  değil, ikinci toplamanın döndürdüğü değerdir.
- **YN26** — Kaynak değerlendirme ölçümünde `kaynak_ver` çağrılma sayısını kendi sayar; bu
  sayı, dış yinelenebilirin ne zaman değerlendirildiğinin ölçüsüdür.
- **YN27** — Son tabloda her işlem iki kap üzerinde ayrı ayrı denenir ve kurulum her
  denemede sıfırlanır; başarısız olan işlemin istisna sınıfı da koşumdan alınır. Yalnız
  standart kitaplık kullanılır.

#### Ölçüm

```python
"""Uretec ifadesi: ayni sonuc, ayni uretim, baska tutma."""

import itertools

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"]


N = 1000


def uretec_tuketimi(n=N):
    sifirla()
    akis = (Oge(i) for i in range(n))
    toplam = sum(o.deger for o in akis)
    return uretilen(), toplam, 0


def kavrama_tuketimi(n=N):
    sifirla()
    liste = [Oge(i) for i in range(n)]
    toplam = sum(o.deger for o in liste)
    return uretilen(), toplam, len(liste)


print(f"{'tüketim biçimi':<22s} {'üretilen':>9s} {'toplam':>8s}"
      f" {'listede tutulan':>16s}")
for ad, f in (("üreteçle toplama", uretec_tuketimi),
              ("kavramayla toplama", kavrama_tuketimi)):
    u, t, tut = f()
    print(f"  {ad:<20s} {u:9d} {t:8d} {tut:16d}")


def zincir_tembel(n=N):
    sifirla()
    ciftler = (o for o in (Oge(i) for i in range(n)) if o.deger % 2 == 0)
    kareler = (o.deger * o.deger for o in ciftler)
    kurulumda = uretilen()
    toplam = sum(kareler)
    return kurulumda, uretilen(), toplam, 0


def zincir_liste(n=N):
    sifirla()
    hepsi = [Oge(i) for i in range(n)]
    ciftler = [o for o in hepsi if o.deger % 2 == 0]
    kareler = [o.deger * o.deger for o in ciftler]
    kurulumda = uretilen()
    toplam = sum(kareler)
    return kurulumda, uretilen(), toplam, len(hepsi) + len(ciftler) + len(kareler)


print()
print(f"{'zincir biçimi':<22s} {'kurulumda':>10s} {'üretilen':>9s}"
      f" {'toplam':>10s} {'kaplarda tutulan':>17s}")
for ad, f in (("üç üreteç ifadesi", zincir_tembel), ("üç liste kavraması", zincir_liste)):
    k, u, t, tut = f()
    print(f"  {ad:<20s} {k:10d} {u:9d} {t:10d} {tut:17d}")


def erken_birakma(n=N):
    sifirla()
    akis = (Oge(i) for i in range(n))
    bulundu = any(o.deger > 4 for o in akis)
    return uretilen(), bulundu


def erken_birakma_liste(n=N):
    sifirla()
    liste = [Oge(i) for i in range(n)]
    bulundu = any(o.deger > 4 for o in liste)
    return uretilen(), bulundu


print()
u1, b1 = erken_birakma()
u2, b2 = erken_birakma_liste()
print(f"tüketici altıncı öğede bıraktığında: üreteç ifadesi {u1} nesne üretti"
      f" (yanıt {b1}), liste kavraması {u2} (yanıt {b2})")

sifirla()
akis = (Oge(i) for i in range(10))
birinci = sum(o.deger for o in akis)
ikinci = sum(o.deger for o in akis)
print(f"aynı üreteç ifadesi iki kez toplandığında: {birinci} ve {ikinci},"
      f" üretilen {uretilen()}")

CAGRI = {"kaynak": 0}


def kaynak_ver():
    CAGRI["kaynak"] += 1
    return [1, 2, 3]


akis = (x for x in kaynak_ver())
kurulumda = CAGRI["kaynak"]
list(akis)
print(f"dış yinelenebilir kurulumda değerlendirildi mi: {kurulumda} çağrı,"
      f" gezildikten sonra {CAGRI['kaynak']}")

kalanlar = [5, 6, 7]
akis = (x * 10 for x in kalanlar)
kalanlar.append(8)
print(f"kaynağa sonradan eklenen öğe akışa girdi mi: {list(akis)}")

sifirla()
kume = {o.deger % 3 for o in (Oge(i) for i in range(9))}
print(f"küme kurucusuna verilen üreteç ifadesi: üretilen {uretilen()},"
      f" tutulan {len(kume)}")

ISLEMLER = (("len(n)", lambda n: len(n)),
            ("n[2]", lambda n: n[2].deger),
            ("n[:3]", lambda n: len(n[:3])),
            ("islice(n, 3)", lambda n: len(list(itertools.islice(n, 3)))))

print()
print(f"{'işlem':<14s} {'liste':>22s} {'üreteç ifadesi':>24s}")
for ad, islem in ISLEMLER:
    satir = []
    for kur in (lambda: [Oge(i) for i in range(N)],
                lambda: (Oge(i) for i in range(N))):
        sifirla()
        n = kur()
        try:
            sonuc = f"{islem(n)} ({uretilen()} üretim)"
        except TypeError as e:
            sonuc = f"{type(e).__name__} ({uretilen()} üretim)"
        satir.append(sonuc)
    print(f"  {ad:<12s} {satir[0]:>22s} {satir[1]:>24s}")

sifirla()
akis = (Oge(i) for i in range(N))
var_mi = any(o.deger == 2 for o in akis)
sinamada = uretilen()
kalan = sum(1 for _ in akis)
print()
print(f"akışta üyelik sınaması: yanıt {var_mi}, sınamada üretilen {sinamada},"
      f" akışta kalan {kalan}")
```

```
tüketim biçimi          üretilen   toplam  listede tutulan
  üreteçle toplama          1000   499500                0
  kavramayla toplama        1000   499500             1000

zincir biçimi           kurulumda  üretilen     toplam  kaplarda tutulan
  üç üreteç ifadesi             0      1000  166167000                 0
  üç liste kavraması         1000      1000  166167000              2000

tüketici altıncı öğede bıraktığında: üreteç ifadesi 6 nesne üretti (yanıt True), liste kavraması 1000 (yanıt True)
aynı üreteç ifadesi iki kez toplandığında: 45 ve 0, üretilen 10
dış yinelenebilir kurulumda değerlendirildi mi: 1 çağrı, gezildikten sonra 1
kaynağa sonradan eklenen öğe akışa girdi mi: [50, 60, 70, 80]
küme kurucusuna verilen üreteç ifadesi: üretilen 9, tutulan 3

işlem                           liste           üreteç ifadesi
  len(n)           1000 (1000 üretim)     TypeError (0 üretim)
  n[2]                2 (1000 üretim)     TypeError (0 üretim)
  n[:3]               3 (1000 üretim)     TypeError (0 üretim)
  islice(n, 3)        3 (1000 üretim)             3 (3 üretim)

akışta üyelik sınaması: yanıt True, sınamada üretilen 3, akışta kalan 997
```

#### Aynı Üretim, Ayrı Tutma

Üst tablo ortak tanımın ikinci iddiasını tamamlıyor ve ilk sütun beklentiyi bozuyor.

**Üretilen sayı ikisinde de 1000.** Tembel olan biçim daha az nesne üretmiyor; bin öğe
istendi, bin öğe doğdu. Toplam sütunu da aynı: ikisi de **499500**. Yani iki yazım aynı işi
yapıyor, aynı sonucu veriyor ve aynı maliyeti ödüyor.

Ayrıldıkları tek sütun sonuncusudur. Toplama bittiğinde kavramayla yazılan biçimde **1000**
öğe hâlâ bir listede duruyor; üreteçle yazılan biçimde **0**. Üretilen bin nesnenin her biri
doğdu, toplamaya katkısını verdi ve ardından hiçbir kaba yazılmadığı için erişilemez oldu.
Kavramada ise hepsi listede kaldı, çünkü listenin işi tam olarak budur.

Buradan ortak tanımın cümlesi çıkar: **tembellik üretimi azaltmaz, tutmayı azaltır.** Bu,
"tembel değerlendirme" adının çağrıştırdığından daha dar bir iddiadır. Ad, işin
yapılmayacağını ima eder; ölçüm, işin yapıldığını ve yalnız **ne zaman** ile **sonrasında ne
kaldığı** sorularının değiştiğini gösterir. **Tembel olan üretimi geciktirir, ondan
kaçınmaz.**

Önceki dersin **0**'ı bu tabloyla birlikte okunmalıdır. Kurulumdaki sıfır bir tasarruf
vaadi değil, bir **erteleme** kaydıdır. Vaat tutulup tutulmadığı akışın nasıl tüketildiğine
bağlıdır, akışın nasıl yazıldığına değil.

#### Zincirin Ara Kapları

Orta tablo tek bir dönüşümü değil, üç halkalı bir zinciri ölçüyor: üret, süz, dönüştür.
Toplam yine aynı — **166167000** — ve üretilen yine **1000**. İki sütun ayrılıyor.

Kurulum sütunu önceki dersin sonucunu tekrarlıyor: tembel zincir **0**, liste zinciri
**1000**. Ama asıl fark son sütundadır. Liste zinciri işini bitirdiğinde **2000** başvuru
kaplarda duruyor: bin öğelik kaynak listesi, beş yüz öğelik süzülmüş liste, beş yüz öğelik
dönüştürülmüş liste. Tembel zincirde bu sayı **0**.

Ara kapların sayısı zincirin uzunluğuyla büyür. Dört halkalı bir liste zinciri dört kap,
beş halkalı beş kap kurar; her halka kendinden öncekinin tamamını görmek ister ve gördüğünü
saklar. Tembel zincirde halka sayısı arttıkça kap sayısı artmaz, çünkü hiçbir halka
kendinden öncekinin tamamını istemez — her seferinde **bir** öğe ister.

Önceki dersin son satırıyla birlikte bu, zincirin kırılma noktasını verir: araya tek bir
`list()` çağrısı girdiğinde o noktada kap kurulur ve zincirin geri kalanının tembelliği o
kapı geri alamaz.

#### Erken Bırakmanın Kaynağı

Bir sonraki satır, iddiaya yapılabilecek en doğal itirazı ölçüyor. Tüketici altıncı öğede
durduğunda üreteç ifadesi **6** nesne üretiyor, liste kavraması **1000**. Burada tembellik
gerçekten üretimden kaçınmış görünüyor.

Ayrım şurada: kaçınmayı sağlayan şey tembellik değil, **tüketicinin erken bırakmasıdır**.
`any` aradığını bulunca sormayı keser; tembel akış da sorulmayanı üretmez. Kavramada ise
tüketici erken bırakmış olsa bile üretim çoktan bitmiştir — liste, `any` daha ilk soruyu
sormadan doldurulmuştur.

İki durum birlikte iddianın tam biçimini verir: tembellik üretimi **tüketiciye bağlar**.
Tüketici her şeyi isterse her şey üretilir (birinci tablo: 1000); erken bırakırsa
üretilmeyen kalır (bu satır: 6). Kavramada üretim tüketiciden bağımsızdır ve her iki
durumda da **1000**'dir. Tembelliğin kazandırdığı, kaçınma değil, **kaçınma olanağıdır**.

Altıncı nesnenin neden üretildiği de aynı düzenin parçasıdır: koşulu sağlayan ilk değer
**5**'tir ve o değeri altıncı nesne taşır; ilk beş nesnenin değerleri 0 ile 4 arasındadır ve
hiçbiri koşulu karşılamaz.

#### Kaynak Ne Zaman Değerlendirilir

Son dört satır üreteç ifadesinin iki ince yanını ölçüyor.

Birincisi tek geçişliliktir. Aynı üreteç ifadesi iki kez toplandığında **45** ve **0**
geliyor; üretilen **10**'da kalıyor. İkinci toplama boş bir akış üzerinde çalışıyor ve
`sum` boş akışta sıfır döndürüyor. Burada bir hata iletisi yoktur — sessiz bir sıfır
vardır. Bu, tek geçişliliğin en sinsi sonucudur: tükenmiş bir akış boş bir akıştan ayırt
edilemez.

İkincisi kaynağın ne zaman okunduğudur. `kaynak_ver` kurulum anında **bir kez** çağrılıyor
ve akış gezildikten sonra sayı yine **1**. Yani üreteç ifadesindeki en dıştaki
yinelenebilir **hemen** değerlendirilir; geciken şey öğelerin çekilmesidir, kaynağın
bulunmasıdır değil. Kaynak adı yanlışsa ya da çağrı hata veriyorsa, bu kurulumda ortaya
çıkar.

Bir sonraki satır bunun sınırını gösteriyor. Kaynak listesi kurulumda değerlendirildi ama
sonradan eklenen öğe akışa **girdi**: sonuç `[50, 60, 70, 80]`. Değerlendirilen şey liste
**nesnesiydi**, içeriğinin anlık kopyası değil. Önceki konunun görünüm ölçümüyle aynı
ayrımdır: bağ kurulmuştur, kopya alınmamıştır. Tembel bir akışın kaynağı akış tüketilmeden
önce değişirse, akış değişmiş hâli görür.

Son satır üreteç ifadesinin nereye verilebileceğini gösteriyor: küme kurucusuna verilen
akış **9** nesne ürettiriyor ve geriye **3** öğe tutuluyor. Var edilen ile tutulan
arasındaki fark burada da açık — kap ne kadarını saklayacağına kendisi karar veriyor.

#### Akışın Yapamadıkları

Son tablo tutmamanın karşılığında nelerden vazgeçildiğini ölçüyor ve üç satırında aynı
istisna duruyor.

`len(n)` listede **1000** veriyor, akışta `TypeError`. Bir akış uzunluğunu bilmez, çünkü
bilmek için sonuna kadar gitmesi ve gittiğini hatırlaması gerekirdi. `n[2]` ve `n[:3]` de
aynı yanıtı veriyor: akış dizinlenemez ve dilimlenemez. Önceki konunun dilimleme dersinde
ölçülen her şey — dilimin yeni kap kurması, iç öğeleri paylaşması — bir akışta hiç
sorulamaz, çünkü dilim geriye dönüp bakmayı gerektirir ve akışın geriye bakacak bir yeri
yoktur.

Üç istisnanın da ortak gerekçesi budur: **sorulan her soru tutmayı gerektirir.** Uzunluk,
dizin ve dilim öğelerin bir arada durmasını varsayar; akış tam olarak bunu yapmayı
reddettiği için bu üç soruyu yanıtlayamaz. Sıfır tutmanın bedeli, üç yerleşik işlemin
kaybıdır.

Dördüncü satır kaybın telafi edilebildiği yeri gösteriyor. Standart kitaplığın `islice`
aracı ilk üç öğeyi akıştan alabiliyor ve akışta yalnız **3** üretim yaptırıyor; listede aynı
sonuç **1000** üretimden sonra geliyor. Dilim gibi görünen bir iş, geriye bakmadan
tanımlandığında akışta da yapılabilir hâle geliyor — ama sonuç bir dilim değil, yine bir
akıştır ve o da tek geçişliktir.

Son satır tek geçişliliğin en dikkat isteyen sonucudur. Akışta üyelik sınaması yanıtı
**True** veriyor ve sınamada yalnız **3** nesne üretiliyor; ama sınamadan sonra akışta
**997** öğe kalıyor. Sınama akışı **tüketti**: sorulan üç öğe geri gelmez. Aynı akış üzerinde
ikinci bir sınama yapılırsa üçüncü öğeden itibaren aranır ve baştaki üç öğe hiç
görülmez. Listede böyle bir yan etki yoktur; akışta sormak, okumaktır.

#### Özet

- Bin öğelik toplamada üreteç de kavrama da **1000** nesne üretir ve ikisi de **499500**
  verir; ayrıldıkları tek yer listede tutulandır — **0**'a karşı **1000**. Tembel olan
  üretimi geciktirir, ondan kaçınmaz.
- Üç halkalı zincirde üretilen yine **1000**'dir ama liste biçimi bitişte **2000** başvuruyu
  kaplarda tutar, tembel biçim **0**. Ara kap sayısı zincir uzadıkça listede büyür,
  akışta büyümez.
- Tüketici altıncı öğede bıraktığında üreteç ifadesi **6**, kavrama **1000** üretir.
  Kaçınmayı sağlayan tembellik değil, tüketicinin durmasıdır; tembellik yalnız bunu
  **olanaklı** kılar.
- Üreteç ifadesi tek geçişliktir ve tükendiğinde sessizce boş davranır: ikinci toplama
  hata değil **0** verir.
- En dıştaki yinelenebilir kurulumda değerlendirilir (**1** çağrı), ama değerlendirilen şey
  nesnenin kendisidir: kaynağa sonradan eklenen öğe akışa girer.

#### Sonraki Adım

Bu iki ders köşeli ayraç ile parantez arasındaki farkı ölçtü, ama köşeli ayracın kendisini
hiç açmadı. Oysa aynı yazım üç ayrı kap kurabilir — liste, küme ve sözlük — ve üçü aynı
kaynaktan aynı öğeleri alıp **farklı sayıda** öğe tutar. Üstelik yazım iç içe geçebilir ve
bir kavramanın içine bir kavrama daha girdiğinde ortaya kurulmamış bir kap çıkar. Sonraki
ders bunları sayar: üç kavrama biçimi aynı bin öğeden kaçını tutar ve süzme koşulunun
nereye yazıldığı üretilen nesne sayısını değiştirir mi?

### Kavrama Sözdizimi

Source: https://academia.sh/tr/kurslar/python-veri-yapilari/kavrama-sozdizimi

Aynı bin öğeden liste kavraması 1000, küme ve sözlük kavraması 100 öğe tutar; süzme koşulunun üretimden önce mi sonra mı yazıldığı var edilen nesneyi 5'ten 10'a çıkarır.

Önceki iki ders köşeli ayraç ile parantez arasındaki farkı ölçtü ve tembelliğin neyi
azaltıp neyi azaltmadığını sayıyla ayırdı. Ama köşeli ayracın kendisi hiç açılmadı: kavrama
her ölçümde yalnız "hepsini şimdi üreten biçim" olarak durdu.

Oysa aynı yazım üç ayrı kap kurabilir. Köşeli ayraç liste, küme ayracı küme, aynı ayraç
içinde iki nokta üst üste ile yazılan çift sözlük üretir. Üçü de aynı kaynaktan aynı öğeleri
alır, ama üçü aynı sayıda öğe **tutmaz**. Bu dersin sorduğu iki soru şudur: üç kavrama
biçimi aynı bin öğeden kaçını tutar ve süzme koşulunun nereye yazıldığı var edilen nesne
sayısını değiştirir mi?

#### Üç Kap, Tek Yazım

Kavrama bir döngü ile bir kap kurulumunun tek deyime sıkıştırılmış hâlidir. Üç biçimin
sözdizimi neredeyse aynıdır ve aralarındaki tek fark ayraç ile öğe ifadesidir. Kavrama
yazımı üreteç ifadesinden yalnız ayraçla ayrılır ve bu tek karakter kurulum davranışını
tamamen değiştirir; önceki ders bunu ölçtü.

Üç kabın ayrıldığı yer öğeyi nasıl kabul ettikleridir. Liste her öğeyi sırayla kabul eder ve
hiçbirini elemez. Küme öğeyi karma değerine göre yerleştirir ve eşiti varsa yenisini
almaz. Sözlük aynı işi anahtar üzerinde yapar, ama değeri **değiştirir**: aynı anahtar
ikinci kez geldiğinde eski değer düşer.

Veri Yapıları kursu bu üç kabın mekanizmasını — karma tablosu, çakışma, yerleşim — kurmuştu;
burada mekanizma tekrarlanmaz. Ölçülen şey aynı kaynağın üç kapta **kaç öğeye** indiğidir.

#### Koşulun İki Yeri

Kavrama iki ayrı yerde koşul kabul eder ve ikisi aynı şey değildir.

**Süzme koşulu** yazımın sonuna gelir ve döngü değişkeni üzerinde çalışır: koşulu geçmeyen
tur öğe ifadesini hiç çalıştırmaz. **Koşullu ifade** ise öğe ifadesinin kendi içindedir ve
her turda bir sonuç seçer; hiçbir turu elemez.

İkisi karıştırıldığında sonuç aynı görünebilir ama sayı görünmez. Ölçüm bunu ayırır.

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

- **YN28** — Ortak tanımın `Oge` sınıfı sayma düzeniyle aynen kullanılır; kâhin kurgunun
  kendisidir.
- **YN29** — Üç kavrama **aynı** bin öğelik kaynak üzerinde çalışır ve kaynak ölçümden önce
  kurulmuştur; bu yüzden üçünün de "var edilen" sütunu sıfırdır. Ölçülen şey tutmadır.
- **YN30** — Küme ve sözlük kavramaları öğeleri `deger % 100` üzerinden gruplar; sayı
  keyfîdir ve yalnız çakışmanın gözlenebilmesi için seçilmiştir.
- **YN31** — "Paylaşılan Oge", kabın içinde kaynaktaki nesnelerin kendisinin durup
  durmadığıdır; türetilmiş bir sayı tutan kapta bu sütun sıfırdır.
- **YN32** — Koşul ölçümü on öğede yapılır ve koşul öğelerin yarısını geçirir; ölçülen şey
  büyüklük değil, iki yazım arasındaki üretim farkıdır.
- **YN33** — İç içe ölçümde tablo **dört satır, üç sütundur**; toplam öğe üç yazımda da
  **12**'dir. "Kurulan kap" sayısına dış kap da dahildir.
- **YN34** — Kapsam ölçümünde kavramanın döngü adı, dışarıda aynı adı taşıyan bir
  değişkenle kasıtlı olarak çakıştırılır.
- **YN35** — Son ölçümde iki yazım **birebir aynıdır**; değişen tek şey iç kaynağın liste
  mi üreteç ifadesi mi olduğudur. Dış döngü üç turludur ve iç kaynak iki öğelidir.

#### Ölçüm

```python
"""Kavrama: uc kap, ayni kaynak, ayri tutma; ve icice kavramanin ara nesnesi."""

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"]


N = 1000
KAYNAK = [Oge(i) for i in range(N)]
KAVRAMALAR = (
    ("liste kavraması", lambda: [o for o in KAYNAK]),
    ("küme kavraması", lambda: {o.deger % 100 for o in KAYNAK}),
    ("sözlük kavraması", lambda: {o.deger % 100: o for o in KAYNAK}))

print(f"{'kavrama':<18s} {'var edilen':>11s} {'kapta tutulan':>14s}"
      f" {'paylaşılan Oge':>15s}")
for ad, kur in KAVRAMALAR:
    sifirla()
    kap = kur()
    ogeler = list(kap.values()) if isinstance(kap, dict) else list(kap)
    ortak = sum(1 for o in ogeler if isinstance(o, Oge))
    print(f"  {ad:<16s} {uretilen():11d} {len(kap):14d} {ortak:15d}")


def kosul_once(n=10):
    sifirla()
    liste = [Oge(i) for i in range(n) if i % 2 == 0]
    return uretilen(), len(liste)


def kosul_sonra(n=10):
    sifirla()
    liste = [o for o in (Oge(i) for i in range(n)) if o.deger % 2 == 0]
    return uretilen(), len(liste)


def kosullu_ifade(n=10):
    sifirla()
    liste = [Oge(i) if i % 2 == 0 else Oge(-i) for i in range(n)]
    return uretilen(), len(liste)


print()
print(f"{'koşulun yeri':<28s} {'var edilen':>11s} {'tutulan':>8s}")
for ad, f in (("üretimden önce süzme", kosul_once),
              ("üretimden sonra süzme", kosul_sonra),
              ("koşullu ifade (süzme yok)", kosullu_ifade)):
    u, t = f()
    print(f"  {ad:<26s} {u:11d} {t:8d}")


def ic_ice(m=4, k=3):
    sifirla()
    tablo = [[Oge(i * k + j) for j in range(k)] for i in range(m)]
    return uretilen(), 1 + len(tablo), sum(len(s) for s in tablo)


def duz(m=4, k=3):
    sifirla()
    liste = [Oge(i * k + j) for i in range(m) for j in range(k)]
    return uretilen(), 1, len(liste)


def duzlestirme(m=4, k=3):
    sifirla()
    tablo = [[Oge(i * k + j) for j in range(k)] for i in range(m)]
    liste = [o for satir in tablo for o in satir]
    return uretilen(), 2 + len(tablo), len(liste)


print()
print(f"{'iç içe yazım':<24s} {'var edilen':>11s} {'kurulan kap':>12s}"
      f" {'öğe':>4s}")
for ad, f in (("satır satır tablo", ic_ice), ("tek düzeyde kavrama", duz),
              ("tabloyu düzleştirme", duzlestirme)):
    u, kap, oge = f()
    print(f"  {ad:<22s} {u:11d} {kap:12d} {oge:4d}")

i = "dışarıdaki değer"
kareler = [i * i for i in range(4)]
print()
print(f"kavramadan sonra dışarıdaki ad: {i!r}, sonuç {kareler}")

sifirla()
sozluk = {o.deger: o for o in (Oge(i % 4) for i in range(12))}
print(f"anahtar çakışması: üretilen {uretilen()}, tutulan {len(sozluk)},"
      f" son değer kazandı -> {sozluk[0].deger}")


def ic_kaynak_liste():
    sifirla()
    ic = [0, 1]
    liste = [Oge(i * 2 + j) for i in range(3) for j in ic]
    return uretilen(), len(liste)


def ic_kaynak_akis():
    sifirla()
    ic = (x for x in [0, 1])
    liste = [Oge(i * 2 + j) for i in range(3) for j in ic]
    return uretilen(), len(liste)


print()
print(f"{'iç kaynağın türü':<28s} {'var edilen':>11s} {'tutulan':>8s}")
for ad, f in (("liste (yeniden gezilebilir)", ic_kaynak_liste),
              ("üreteç ifadesi (tek geçişlik)", ic_kaynak_akis)):
    u, t = f()
    print(f"  {ad:<30s} {u:9d} {t:8d}")
```

```
kavrama             var edilen  kapta tutulan  paylaşılan Oge
  liste kavraması            0           1000            1000
  küme kavraması             0            100               0
  sözlük kavraması           0            100             100

koşulun yeri                  var edilen  tutulan
  üretimden önce süzme                 5        5
  üretimden sonra süzme               10        5
  koşullu ifade (süzme yok)           10       10

iç içe yazım              var edilen  kurulan kap  öğe
  satır satır tablo               12            5   12
  tek düzeyde kavrama             12            1   12
  tabloyu düzleştirme             12            6   12

kavramadan sonra dışarıdaki ad: 'dışarıdaki değer', sonuç [0, 1, 4, 9]
anahtar çakışması: üretilen 12, tutulan 4, son değer kazandı -> 0

iç kaynağın türü              var edilen  tutulan
  liste (yeniden gezilebilir)            6        6
  üreteç ifadesi (tek geçişlik)          2        2
```

#### Üç Kabın Tuttuğu

Üst tabloda "var edilen" sütunu üç satırda da **0**. Bu bir tuhaflık değil, kurgunun
kendisidir: bin öğe ölçümden önce kurulmuştu ve üç kavrama da yeni bir `Oge` üretmiyor,
yalnız var olanları alıyor. Ölçülen şey burada üretim değil, **tutma**.

Liste kavraması **1000** öğe tutuyor ve bunların **1000**'i kaynaktaki nesnenin kendisi.
Yeni bir kap kuruldu, içine bin başvuru yazıldı, hiçbir öğe çoğaltılmadı — ilk dersin
ölçtüğü kap kurma işleminin ta kendisi.

Küme kavraması **100** öğe tutuyor ve paylaşılan **0**. İki sayı birlikte okunmalı:
kavramanın öğe ifadesi `o.deger % 100` bir sayı üretiyor, `Oge` değil; kap kaynaktaki
nesneleri hiç almıyor. Bin turdan yalnız yüz ayrı değer çıkıyor, çünkü küme eşitini ikinci
kez kabul etmiyor. Bin öğe okundu, yüz öğe tutuldu — ve okunan ile tutulan arasındaki fark
**900**.

Sözlük kavraması da **100** tutuyor ama paylaşılan **100**. Anahtar türetilmiş bir sayı,
değer ise kaynaktaki nesnenin kendisi. Yani sözlük hem eledi hem paylaştı: yüz anahtarın
her biri, o anahtara düşen son `Oge` nesnesine bağlı.

Son satır bunun sonucunu doğrudan gösteriyor. On iki öğe üretildi, dördü tutuldu ve
çakışan anahtarlarda **son değer kazandı**. Sözlük kavramasında elemeyi yapan şey sessizdir:
düşen öğeler için ne uyarı vardır ne istisna. Bin satırlık bir kaynaktan yüz satırlık bir
sözlük çıktığında yazım aynı kalır, kaybın izi ancak sayılırsa görünür.

#### Koşul Nereye Yazılırsa

Orta tablo üç satırında da aynı on turu döner ama üretim sayıları ayrılır.

**Üretimden önce süzme** — koşul yazımın sonunda ve döngü değişkeni üzerinde — **5** nesne
var ediyor. Koşulu geçmeyen beş tur öğe ifadesine hiç ulaşmıyor; `Oge` çağrısı yapılmıyor.

**Üretimden sonra süzme** aynı beş öğeyi tutuyor ama **10** nesne var ediyor. Burada koşul
öğenin kendisine bakıyor, dolayısıyla öğenin önce var olması gerekiyor. Beş nesne
doğuyor, süzülüyor ve hiçbir kaba girmediği için erişilemez oluyor.

İki satırın tuttuğu sayı aynı: **5**. Sonuç da aynı. Ayrılan tek şey elemenin **hangi
tarafta** yapıldığı. Süzme koşulu kaynağın diliyle yazılabiliyorsa üretimden kaçınılır;
yalnız üretilmiş nesnenin özelliğine bakılabiliyorsa kaçınılamaz. Bu, önceki dersin erken
bırakma ölçümünün kavrama içindeki karşılığıdır — tembellik yine bir olanaktır, koşulun
nereye yazılabildiğine bağlı.

**Koşullu ifade** satırı üçüncü davranışı gösteriyor: **10** üretim, **10** tutma. Buradaki
`if` bir süzgeç değil, bir seçicidir; hiçbir turu elemez, yalnız hangi nesnenin
üretileceğini seçer. İki yazım birbirine benzer ve konumları farklıdır: seçici öğe
ifadesinin içinde, süzgeç yazımın sonundadır. Ayrımı sayı verir — süzgeç tutulanı azaltır,
seçici azaltmaz.

#### İç İçe Kavramanın Ara Kapları

Alt tablo aynı on iki öğeyi üç yazımla kuruyor. "Var edilen" sütunu üçünde de **12** ve öğe
sayısı üçünde de **12**; ayrılan sütun kap sayısıdır.

**Satır satır tablo** — bir kavramanın içinde başka bir kavrama — **5** kap kuruyor: dört
satır listesi ve onları taşıyan dış liste. İç kavrama her dış turda yeniden çalışıyor ve her
seferinde yeni bir liste üretiyor. Bu listeler ara nesne değil, sonucun kendisidir; tablo
yapısı isteniyorsa gereklidir.

**Tek düzeyde kavrama** — iki `for` yan yana, tek öğe ifadesi — aynı on iki öğeyi **1** kap
kuruyor. İki döngü tek kavrama içinde iç içe dönüyor ve ara liste hiç doğmuyor. Sıra da
aynı: soldaki `for` dış döngüdür.

**Tabloyu düzleştirme** üçüncü yolu gösteriyor: önce tablo kurulur, sonra düzleştirilir.
Sonuç ikinci satırla aynı, kap sayısı **6**. Beş kap kuruldu, biri daha eklendi ve kurulan
dört satır listesi düzleştirmeden sonra hiçbir işe yaramıyor — bunlar gerçek **ara
nesnelerdir**. Aynı sonucun tek kapla kurulabildiği yerde beşi fazladan.

Ayrım şuraya oturur: iç içe kavrama **yapıyı** kurar, yan yana `for` **geziyi** kurar. Sonuç
düz bir dizi olacaksa yapıyı kurup sonra yıkmak, hiç kurmamaktan pahalıdır.

#### İç Kaynak Kaç Kez Gezilir

Son tablo yan yana iki `for` yazımının en kolay gözden kaçan yanını ölçüyor: iki yazım
birebir aynı, sonuçlar **6**'ya karşı **2**.

Fark iç kaynağın türünden geliyor. Sağdaki `for` her dış turda **yeniden gezilir**; üç dış
tur için iç kaynaktan üç kez baştan geçilmesi gerekir. İç kaynak bir listeyse bu sorunsuz
olur — liste her istendiğinde taze bir yineleyici verir ve üç kez ikişer öğe, toplam **6**
öğe çıkar.

İç kaynak bir üreteç ifadesiyse olmaz. İlk dış turda akış tüketilir; ikinci ve üçüncü
turlarda aynı akıştan yeniden geçilmek istenir ve akış **boş** döner. Sonuç **2** öğe ve
ortada ne istisna vardır ne uyarı. Birinci derste ölçülen tek geçişlilik burada bir sonuç
üretiyor: tükenmiş akış boş akıştan ayırt edilemediği için kavrama sessizce eksik bir liste
kuruyor.

Bu, üç dersin biriktirdiği ayrımın pratikteki karşılığıdır. Bir akış yeniden gezilecek bir
yere konuyorsa — iç içe bir döngünün iç kaynağı, iki kez okunacak bir değişken — önce
listeye alınmalıdır. Alınmazsa ikinci geçiş hata değil, **sessiz bir eksik** üretir.

#### Kavramanın Kendi Kapsamı

Son iki satır Python'a özgü bir ayrıntıyı ölçüyor. Kavramanın döngü değişkeni dışarıdaki
aynı adlı değişkeni **ezmiyor**: kavramadan sonra `i` hâlâ kendi değerini taşıyor, kavrama
ise kendi `i` adıyla `[0, 1, 4, 9]` üretiyor.

Bunun nedeni kavramanın kendi kapsamına sahip olmasıdır. Python Temelleri kursunun kapsam
dersi adın hangi düzeyde arandığını kurmuştu; burada eklenen tek şey kavramanın bu
düzeylerden birini **kendisi açtığıdır**. Döngü adı kavramanın içinde doğar ve orada ölür.

Sıradan bir `for` döngüsünde durum böyle değildir: orada döngü değişkeni çevreleyen kapsama
yazılır ve döngü bittikten sonra da yaşar. Aynı işi yapan iki yazımdan biri ad bırakıyor,
öbürü bırakmıyor. Bu, kavramanın yalnız bir kısaltma olmadığını gösterir — ad görünürlüğü
bakımından ayrı bir düzenektir.

#### Özet

- Aynı bin öğelik kaynaktan liste kavraması **1000**, küme ve sözlük kavraması **100** öğe
  tutar; paylaşılan `Oge` sayısı sırasıyla **1000**, **0** ve **100**'dür. Kap ne kadarını
  saklayacağına kendisi karar verir ve eleme sessizdir.
- Süzme koşulu üretimden önce yazılabiliyorsa var edilen **5**'te kalır; yalnız üretilmiş
  nesneye bakılabiliyorsa **10** olur. Tutulan iki yazımda da **5**'tir.
- Öğe ifadesinin içindeki koşullu ifade bir süzgeç değildir: hiçbir turu elemez, **10**
  üretir ve **10** tutar.
- İç içe kavrama on iki öğe için **5** kap kurar, yan yana iki `for` aynı öğeleri **1** kapla
  verir; önce kurup sonra düzleştirmek **6** kap eder ve dördü ara nesnedir.
- Kavramanın kendi kapsamı vardır: döngü adı dışarıdaki aynı adlı değişkeni ezmez ve
  kavrama bitince kaybolur; sıradan `for` döngüsünde ad geride kalır.
- Yan yana iki `for` yazımında sağdaki kaynak her dış turda yeniden gezilir: liste **6**
  öğe verir, aynı yazımda üreteç ifadesi **2**. Tükenmiş akış sessizce boş davrandığı için
  eksik sonuç istisna üretmez.

#### Sonraki Adım

Bu derste süzme koşulu da öğe ifadesi de kavramanın **içine** yazıldı; her ikisi de yazıldığı
yere gömülü kaldı ve başka bir kavramada yeniden kullanılamadı. Oysa ikisi de bir kural
ifade ediyor — "şunu geçir", "şunu şuna dönüştür" — ve kurallar adlandırılabilir. Sonraki
ders kuralı bir nesne olarak ele alır: bir işlev bir başka işleve argüman olarak
geçirilebilir mi ve geçirildiğinde **kaç kez çağrılır**? Sıralamada anahtar hesabı öğe
başına bir kez mi yapılır, yoksa her karşılaştırmada yeniden mi?

### Lambda ve Yüksek Mertebeden Fonksiyonlar

Source: https://academia.sh/tr/kurslar/python-veri-yapilari/lambda-ve-yuksek-mertebeden-fonksiyonlar

İşlev bir nesnedir ve argüman olarak geçirilebilir: iki yüz öğede anahtar işlevi sorted, min ve max için öğe başına tam bir kez çağrılır — aynı işi elle yazan sıralama onu 39800 kez çağırır.

Önceki derste süzme koşulu da öğe ifadesi de kavramanın içine gömülü kaldı. İkisi de bir
kural anlatıyordu — "şunu geçir", "şunu şuna dönüştür" — ama kural yazıldığı yerin dışında
kullanılamıyordu. Aynı süzgeci ikinci bir kavramada tekrarlamak, onu tekrar yazmak
demekti.

Kurallar adlandırılabilir. Bir işlev tam olarak budur: adlandırılmış bir kural. Bu dersin
sorusu, o işlevin bir **argüman** olarak geçirilip geçirilemeyeceği ve geçirildiğinde
**kaç kez çağrıldığıdır**. Sıralamada anahtar hesabı öğe başına bir kez mi yapılır, yoksa
her karşılaştırmada yeniden mi? Aradaki fark iki yüz öğede iki sayı üretecek ve ikisi aynı
büyüklükte olmayacak.

#### İşlev Bir Nesnedir

Python'da `def` bir bildirim değil, bir **deyimdir**: çalıştığında bir işlev nesnesi üretir
ve onu bir ada bağlar. Bağlama başka her bağlama gibidir — nesne bir listeye konabilir, bir
sözlükte değer olabilir, bir işleve argüman olarak geçirilebilir, bir işlevden döndürülebilir.

`lambda` aynı nesneyi ad bağlamadan üretir. Tek bir ifade taşır, `return` yazılmaz, sonucu
ifadenin değeridir. Üretilen nesne `def` ile üretilenle **aynı türdendir**; ayrıldığı tek
yer taşıdığı addır.

Bir işlevi argüman olarak alan ya da işlev döndüren işleve **yüksek mertebeden fonksiyon
(higher-order function)** denir. Programlama Temelleri kursu bu kavramı fonksiyonel
programlamaya girişte kurdu; burada kavram tekrarlanmaz. Ölçülen şey Python'un yerleşik
yüksek mertebeden fonksiyonlarının verilen işlevi **kaç kez çağırdığı** ve **kaç nesne var
ettiğidir**.

#### Anahtar İşlevinin Sözleşmesi

`sorted`, `min` ve `max` bir `key` argümanı kabul eder: her öğe için hesaplanacak
karşılaştırma değerini veren işlev. Sözleşmenin ölçülebilir yanı şudur — bu işlev öğe başına
**kaç kez** çağrılır?

İki yanıt akla gelir. Sıralama öğeleri birbiriyle karşılaştırır ve karşılaştırma sayısı öğe
sayısından fazladır; anahtar her karşılaştırmada hesaplanıyorsa çağrı sayısı da öyle olur.
Ya da anahtar bir kez hesaplanıp saklanır ve karşılaştırmalar saklanan değerler üzerinde
yapılır. İkinci yol çağrı sayısını düşürür ama **tutmayı** artırır: her öğe için bir anahtar
değeri canlı tutulmalıdır. Ölçüm hangisinin geçerli olduğunu söyler.

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

- **YN36** — Ortak tanımın `Oge` sınıfı sayma düzeniyle aynen kullanılır; `Oge` yalnız
  üretim sayar, anahtar çağrısı ayrı bir sayaçta tutulur.
- **YN37** — Anahtar işlevi her çağrılışını kendi sayar; "kaç kez çağrıldı" sorusunun ölçüsü
  bu sayaçtır ve kâhin kurgunun kendisidir.
- **YN38** — Kaynak **200** öğelidir ve dört işlemin dördü de aynı kaynak üzerinde çalışır.
  Sayaç her işlemden önce sıfırlanır.
- **YN39** — Anahtar değeri `-o.deger % 97` ile hesaplanır; sayı keyfîdir ve yalnız
  sıralamanın kaynak sırasından farklı bir sıra üretmesi için seçilmiştir.
- **YN40** — Elle yazılan seçmeli sıralama karşılaştırma başına anahtarı **iki kez**
  hesaplar; bu, anahtarı saklamayan bir yazımın davranışıdır ve ölçümün karşı ucudur.
  Sıralama bu derste bir algoritma olarak değil, **çağrı sayısı üreten bir kurgu** olarak
  kullanılır.
- **YN41** — `sorted` ile `liste.sort` ayrımı kimlikle ölçülür; **kimlik `is` ile
  gösterilir, sayısı basılmaz.**
- **YN42** — Eşleme ve süzme ölçümünde kurulum satırından hemen sonra sayaç okunur; öğe
  üreten işlev kasıtlı olarak yeni `Oge` üretir, böylece tembellik üretim üzerinden
  görünür.
- **YN43** — Üç işlevin ayrı nesneler olduğu ikişerli `is` karşılaştırmalarıyla gösterilir;
  kimlik sayısı basılmaz.
- **YN44** — Bağlama ölçümünde üç yazım da **aynı** üç işlevi kurar ve hepsi aynı öğeyle
  çağrılır; değişen tek şey çarpanın nereye yazıldığıdır.

#### Ölçüm

```python
"""Islev bir nesnedir: anahtar islevi kac kez cagrilir, akis ne zaman var eder."""

import functools

SAYAC = {"uretilen": 0, "anahtar": 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
    SAYAC["anahtar"] = 0


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


def anahtar(o):
    """Ogenin siralama anahtari; her cagrilisini sayar."""
    SAYAC["anahtar"] += 1
    return -o.deger % 97


N = 200
KAYNAK = [Oge(i) for i in range(N)]


def elle_sec(ogeler, islev):
    """Her karsilastirmada anahtari yeniden hesaplayan secmeli siralama."""
    kalan, sirali = list(ogeler), []
    while kalan:
        enk = 0
        for i in range(1, len(kalan)):
            if islev(kalan[i]) < islev(kalan[enk]):
                enk = i
        sirali.append(kalan.pop(enk))
    return sirali


ISLEMLER = (("sorted(n, key=f)", lambda n: len(sorted(n, key=anahtar))),
            ("min(n, key=f)", lambda n: min(n, key=anahtar)),
            ("max(n, key=f)", lambda n: max(n, key=anahtar)),
            ("elle seçmeli sıralama", lambda n: len(elle_sec(n, anahtar))))

print(f"{'işlem':<24s} {'anahtar çağrısı':>16s} {'öğe başına':>11s}")
for ad, islem in ISLEMLER:
    sifirla()
    islem(KAYNAK)
    print(f"  {ad:<22s} {SAYAC['anahtar']:16d} {SAYAC['anahtar'] / N:11.1f}")

sifirla()
sirali = sorted(KAYNAK, key=anahtar)
print()
print(f"sorted(n) is n -> {sirali is KAYNAK}, var edilen {uretilen()},"
      f" paylaşılan {sum(1 for o in sirali if any(o is k for k in KAYNAK))}")
kopya = list(KAYNAK)
once = kopya
sonuc = kopya.sort(key=anahtar)
print(f"liste.sort dönen değer {sonuc!r}, kap aynı kaldı -> {once is kopya}")


def adli(o):
    return o.deger


adsiz = lambda o: o.deger
print()
print(f"{'işlev':<12s} {'çağrılabilir':>13s} {'__name__':>12s} {'tür aynı mı':>12s}")
for ad, f in (("def ile", adli), ("lambda ile", adsiz)):
    print(f"  {ad:<10s} {str(callable(f)):>13s} {f.__name__:>12s}"
          f" {str(type(f) is type(adli)):>12s}")

DEFTER = {"artir": lambda o: o.deger + 1, "iki_kat": lambda o: o.deger * 2}
print(f"sözlükte tutulan işlevler: {sorted(DEFTER)},"
      f" DEFTER['iki_kat'](Oge(21)) -> {DEFTER['iki_kat'](Oge(21))}")

sifirla()
esleme = map(lambda o: Oge(o.deger * 2), KAYNAK)
suzme = filter(lambda o: o.deger % 2 == 0, KAYNAK)
kurulumda = uretilen()
ilk = next(esleme)
print()
print(f"map ve filter kurulumda {kurulumda} nesne var etti,"
      f" ilk öğe istendiğinde {uretilen()}")
print(f"tümü gezildiğinde: map {len(list(esleme)) + 1} öğe,"
      f" filter {len(list(suzme))} öğe, üretilen {uretilen()}")

sifirla()
toplam = functools.reduce(lambda a, o: a + o.deger, KAYNAK, 0)
print(f"reduce ile toplam {toplam}, var edilen {uretilen()}")


def carpan_ureten(k):
    def carp(o):
        return o.deger * k
    return carp


uc = [carpan_ureten(k) for k in (1, 2, 3)]
ayri = all(a is not b for i, a in enumerate(uc) for b in uc[i + 1:])
print()
print(f"üç işlev birbirinden ayrı nesne -> {ayri},"
      f" sonuçlar {[f(Oge(10)) for f in uc]}")
print(f"carpan_ureten(2) is carpan_ureten(2) -> "
      f"{carpan_ureten(2) is carpan_ureten(2)}")

gec = [lambda o: o.deger * k for k in (1, 2, 3)]
erken = [lambda o, k=k: o.deger * k for k in (1, 2, 3)]
kismi = [functools.partial(lambda o, k: o.deger * k, k=k) for k in (1, 2, 3)]
print()
print(f"{'bağlama biçimi':<26s} {'nesne':>6s} {'sonuçlar':>16s}")
for ad, kap in (("gövdede serbest ad", gec), ("varsayılan argüman", erken),
                ("partial ile", kismi)):
    print(f"  {ad:<24s} {len(kap):6d} {str([f(Oge(10)) for f in kap]):>16s}")
```

```
işlem                     anahtar çağrısı  öğe başına
  sorted(n, key=f)                    200         1.0
  min(n, key=f)                       200         1.0
  max(n, key=f)                       200         1.0
  elle seçmeli sıralama             39800       199.0

sorted(n) is n -> False, var edilen 0, paylaşılan 200
liste.sort dönen değer None, kap aynı kaldı -> True

işlev         çağrılabilir     __name__  tür aynı mı
  def ile             True         adli         True
  lambda ile          True     <lambda>         True
sözlükte tutulan işlevler: ['artir', 'iki_kat'], DEFTER['iki_kat'](Oge(21)) -> 42

map ve filter kurulumda 0 nesne var etti, ilk öğe istendiğinde 1
tümü gezildiğinde: map 200 öğe, filter 100 öğe, üretilen 200
reduce ile toplam 19900, var edilen 0

üç işlev birbirinden ayrı nesne -> True, sonuçlar [10, 20, 30]
carpan_ureten(2) is carpan_ureten(2) -> False

bağlama biçimi              nesne         sonuçlar
  gövdede serbest ad            3     [30, 30, 30]
  varsayılan argüman            3     [10, 20, 30]
  partial ile                   3     [10, 20, 30]
```

#### Öğe Başına Bir Çağrı

Üst tablonun ilk üç satırı aynı sayıyı veriyor: **200** çağrı, öğe başına **tam 1.0**.
Sıralama, en küçüğü bulma ve en büyüğü bulma — üçünde de anahtar işlevi her öğe için bir
kez çağrılıyor, daha fazla değil.

`min` ve `max` için bu beklenendir; her öğeye bir kez bakmak yeter. `sorted` için değildir.
Sıralama öğeleri birbiriyle karşılaştırmak zorundadır ve karşılaştırma sayısı öğe sayısından
fazladır. Yine de anahtar çağrısı **200**'de kalıyor: demek ki anahtarlar bir kez hesaplanıp
**saklanıyor** ve bütün karşılaştırmalar saklanan değerler üzerinde yapılıyor.

Dördüncü satır bu kararın karşı ucunu gösteriyor. Aynı işi yapan, anahtarı hiç saklamayan ve
her karşılaştırmada yeniden hesaplayan bir sıralama, aynı iki yüz öğede anahtarı **39800**
kez çağırıyor — öğe başına **199.0**. İki sayı arasındaki oran iki yüzdür ve bu oran öğe
sayısı büyüdükçe büyür.

Kursun ekseninden okunuşu şudur: yerleşik `sorted` bir **takas** yapıyor. Anahtar değerlerini
saklamak öğe sayısı kadar başvuru tutmak demektir; karşılığında çağrı sayısı öğe sayısına
iner. Tutmadan kaçınan yazım — dördüncü satır — hiçbir ara değer saklamıyor ve bedelini
çağrı sayısıyla ödüyor. Bu, ikinci dersin tersine çevrilmiş hâlidir: orada tutmayan biçim
kazanıyordu, burada tutan biçim kazanıyor. **Neyin tutulduğu kadar, tutmanın neyi ortadan
kaldırdığı da sayılmalıdır.**

Bu ölçüm bir sıralama algoritması karşılaştırması değildir; algoritmalar ve karmaşıklık
çözümlemesi Algoritmalar kursunun konusudur. Buradaki dördüncü satır yalnız anahtarı
saklamayan bir yazımın kaç çağrı ürettiğini göstermek için elle kuruldu.

#### Yeni Kap mı, Yerinde mi

Sonraki iki satır aynı işin iki yazımını ayırıyor. `sorted` yeni bir liste üretiyor:
`sorted(n) is n` **yanlış**, var edilen `Oge` **0** ve paylaşılan **200**. Sıralama öğeleri
çoğaltmıyor, yalnız yeni bir kaba başka sırayla yazıyor — birinci dersin kap kurma ölçümünün
aynısı.

`liste.sort` yeni kap kurmuyor: kap aynı kalıyor ve dönen değer **`None`**. Bu, Python'da
yerinde çalışan yöntemlerin ortak imzasıdır ve bir sonucu vardır — `n = n.sort()` yazımı
listeyi `None` ile değiştirir. Ayrım tek satırda görünür: sonuç isteniyorsa `sorted`, kabı
korumak isteniyorsa `sort`.

#### İşlevin Nesne Olduğunun Kanıtı

Orta tablo iki işlevi karşılaştırıyor ve üç sütunun ikisinde aynı yanıtı veriyor. `def` ile
tanımlanan ve `lambda` ile üretilen nesnelerin ikisi de **çağrılabilir**, ikisi de **aynı
türden**. Ayrıldıkları tek sütun `__name__`: biri kendi adını taşıyor, öbürü `<lambda>`.

Bu tek fark önemsiz görünür ama bir sonraki dersin ölçtüğü şeyin habercisidir. Bir işlev
nesnesi kendi adını bir öznitelikte taşır ve o öznitelik hata izlerinde, günlük kayıtlarında
ve belge üretiminde kullanılır. Adı olmayan bir nesne bu bilgiyi veremez.

Sonraki satır nesne olmanın pratikteki karşılığını gösteriyor: iki işlev bir sözlükte değer
olarak duruyor ve anahtarla çağrılıyor. Kural artık veriyle aynı yerde yaşıyor —
adlandırılabilir, saklanabilir, geçirilebilir. Önceki dersin kavramaya gömülü kalan süzgeci
burada bir sözlük değeri olabilirdi.

#### Tembel Yüksek Mertebeden Yerleşikler

Son üç satır `map` ve `filter` yerleşiklerini ölçüyor ve tanıdık bir sayı çıkıyor: kurulumda
**0** nesne. İkisi de birer akış döndürüyor; verilen işlev kurulum anında hiç çağrılmıyor.
İlk öğe istendiğinde üretilen **1** oluyor, tümü gezildiğinde **200**.

Bu, üçüncü dersin iddiasının yerleşikler üzerindeki karşılığıdır: `map` üretimden kaçınmaz,
onu geciktirir. İki yüz öğe istendiğinde iki yüz nesne doğar. Süzme de aynı düzendedir ve
iki yüz öğeden **100**'ünü geçirir; geçmeyenler için yeni nesne kurulmadığı için üretim
sayısı yine iki yüzde kalır.

`map` ile bir kavrama arasındaki seçim bu yüzden bir maliyet seçimi değil, bir yazım
seçimidir — biri akış verir, öbürü kap. İkisinin sayıları önceki iki derste ölçüldü ve
burada değişmiyor.

Son satır üçüncü bir yüksek mertebeden aracı gösteriyor: standart kitaplığın `reduce`
işlevi, iki argümanlı bir işlevi akış boyunca uygulayarak tek bir değere iner. Toplam
**19900** ve var edilen **0** — hiçbir ara kap kurulmuyor, her adımda yalnız biriken değer
taşınıyor. Kavrama bir kap, `map` bir akış, `reduce` bir **değer** üretir; üçü de aynı
işlevi argüman olarak alır.

#### İşlev Döndüren İşlev

Son iki blok yönü tersine çeviriyor: işlev artık argüman değil, **dönen değer**.

`carpan_ureten` çağrıldığında gövdesindeki `def` çalışır ve yeni bir işlev nesnesi üretir.
Üç çağrı **üç ayrı nesne** veriyor ve üçü ayrı sonuçlar üretiyor — her biri kendi çarpanını
taşıyor. Aynı argümanla yapılan iki çağrı bile ayrı nesne veriyor:
`carpan_ureten(2) is carpan_ureten(2)` **yanlış**. İşlev üretmek, her seferinde bir nesne
var etmektir; on ayrı çarpan on ayrı işlev nesnesi demektir.

Programlama Temelleri kursu kapanışı — iç işlevin dış kapsamdaki adı taşıması — bir kavram
olarak kurmuştu; burada eklenen tek şey o taşımanın **nesne sayısıyla** ölçülmesi ve
taşınanın bir **değer değil, bir ad** olmasıdır. Son tablo bu ayrımın sonucunu veriyor.

Üç yazım da üç işlev üretiyor ve üçünde de nesne sayısı **3**. Ama sonuçlar aynı değil.
**Gövdede serbest ad** kullanan biçim `[30, 30, 30]` veriyor: üç işlevin üçü de aynı `k`
adına bakıyor ve çağrıldıkları anda o adın son değeri **3**. Kapanış `k`'nin değerini
kopyalamadı, **adına bağlandı**.

**Varsayılan argüman** biçimi `[10, 20, 30]` veriyor. Python Temelleri kursunun fonksiyon
tanımı dersi varsayılan değerin **bir kez, tanım anında** değerlendirildiğini ölçmüştü;
orada bu bir tuzaktı, burada çözümdür. Değer tanım anında hesaplandığı için her işlev kendi
çarpanını yanında taşır. Standart kitaplığın `partial` aracı aynı sonucu argümanı önceden
bağlayarak verir.

Üç satır aynı yazımın üç okunuşudur ve aralarındaki fark ad ile değer arasındaki farktır.
Kavramada döngü adı kavrama bitince kaybolur; ona bağlanan işlev kaybolmaz ve son değeri
görür. **Bir ada bağlanmak, bir değeri almak değildir.**

#### Özet

- `def` bir deyimdir ve bir işlev nesnesi üretir; `lambda` aynı türden nesneyi ad bağlamadan
  verir. İkisi de çağrılabilir, ikisi de aynı türdendir ve ayrıldıkları tek yer
  `__name__`'dir.
- İşlev nesnesi bir kapta değer olarak tutulabilir ve argüman olarak geçirilebilir; bir işlevi
  argüman alan ya da döndüren işlev yüksek mertebeden fonksiyondur.
- İki yüz öğede anahtar işlevi `sorted`, `min` ve `max` için **200** kez — öğe başına tam
  **bir** kez — çağrılır. Anahtarı saklamayan elle yazılmış sıralama aynı işlevi **39800**
  kez çağırır.
- `sorted` yeni kap kurar ve **200** öğeyi paylaşır, hiçbirini çoğaltmaz; `liste.sort`
  yerinde çalışır ve **`None`** döndürür.
- `map` ve `filter` kurulumda **0** nesne var eder, tümü gezildiğinde **200**; süzme iki
  yüz öğeden **100**'ünü geçirir. `reduce` ise hiç kap kurmadan tek değere iner.

#### Sonraki Adım

Bu derste işlevler hep argüman olarak geçti; hiçbiri değiştirilmedi. Oysa bir işlev
döndürülebiliyorsa, bir işlevi alıp **yerine geçecek başka bir işlev** döndürmek de
mümkündür — özgün davranışı koruyup çevresine bir şeyler ekleyen bir sarmalayıcı. Sonraki
ders bu kalıbı ölçer: bir işlevi sarmalamak kaç nesne var eder, özgün nesneye ne olur ve
sarmalanmış işlevin `__name__` özniteliği ne söyler? Bu dersin önemsiz görünen tek sütunu
orada bir sonuç üretecek.

### Dekoratörler

Source: https://academia.sh/tr/kurslar/python-veri-yapilari/dekoratorler

Sarmalama özgün işlevi silmez, üstüne bir katman koyar: tek dekoratör iki katman kurar, iki dekoratör üç ve meta veri korunmadığında işlevin adı, belgesi ve imzası sarmalayıcınınkiyle değişir.

Önceki derste işlevler hep argüman olarak geçti ve hiçbiri değiştirilmedi. Ama aynı ders bir
işlevin **döndürülebildiğini** de ölçtü: `carpan_ureten` her çağrıda yeni bir işlev nesnesi
üretiyordu. İki yönü birleştirmek mümkün — bir işlevi **alan** ve onun **yerine geçecek**
başka bir işlev **döndüren** bir işlev.

Böyle bir işleve **dekoratör (decorator)** denir ve döndürdüğü nesneye **sarmalayıcı
(wrapper)** denir. Bu dersin soruları şunlardır: sarmalamak kaç nesne var eder, özgün işleve
ne olur ve sarmalanmış işlevin `__name__` özniteliği ne söyler? Önceki dersin önemsiz
görünen tek sütunu — `def` ile `lambda` arasındaki ad farkı — burada bir sonuç üretecek.

#### Sarmalayıcı Kalıbı

Kalıp üç adımlıdır. Dekoratör bir işlev alır; gövdesinde yeni bir işlev tanımlar; o yeni
işlevi döndürür. İçteki işlev çağrıldığında kendi işini yapar ve **aldığı işlevi de** çağırır
— böylece özgün davranış korunur, çevresine bir şey eklenir.

Adın önüne `@` konarak yazılan biçim bir kısaltmadır. `@dekorator` satırı bir işlev tanımının
üstüne konduğunda, tanım bittikten sonra `hesapla = dekorator(hesapla)` yapılır. Yani ortada
yeni bir düzenek yoktur; önceki dersin yüksek mertebeden fonksiyonu, sonucu aynı ada geri
bağlanmıştır.

Buradan önemli bir sonuç çıkar: **özgün işlev yok edilmez.** Ad artık sarmalayıcıya
bağlıdır, ama sarmalayıcının gövdesi özgün nesneye başvurur ve onu canlı tutar. Sarmalama
bir silme değil, bir **katman eklemedir** — ve katmanlar sayılabilir.

#### İki Ayrı Zaman

Sarmalama iki ayrı anda iş yapar ve karıştırılmaları sık görülür.

**Tanım anı:** dekoratörün gövdesi çalışır ve sarmalayıcıyı üretir. Bu bir kez olur —
işlevin tanımlandığı yerde, hiç çağrılmasa bile.

**Çağrı anı:** sarmalayıcının gövdesi çalışır. Bu her çağrıda olur.

Ayrımın ölçülebilir sonucu şudur: dekoratör gövdesindeki iş kaç işlev sarmalanmışsa o kadar
kez, sarmalayıcı gövdesindeki iş kaç çağrı yapılmışsa o kadar kez yapılır.

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

- **YN45** — Ortak tanımın `Oge` sınıfı sayma düzeniyle aynen kullanılır; dekoratör
  gövdesi, sarmalayıcı ve özgün gövde ayrı sayaçlarda tutulur. Kâhin kurgunun kendisidir.
- **YN46** — Katman sayısı `__wrapped__` zinciri izlenerek bulunur; bu zincir yalnız meta
  veri koruma kullanıldığında kurulur.
- **YN47** — Dört yazımın dördü de **aynı** özgün işlevi sarmalar ve dördü de bir kez
  çağrılır. Sayaçlar ölçümden önce sıfırlanır.
- **YN48** — Parametreli dekoratör özgün gövdeyi kasıtlı olarak **üç kez** çağırır; sayı
  keyfîdir ve sarmalayıcının davranış eklediğini görünür kılmak için seçilmiştir.
- **YN49** — Meta veri ölçümünde üç yazım karşılaştırılır: sarmasız, koruma kullanmayan ve
  kullanan. **Kimlik `is` ile gösterilir, sayısı basılmaz.**
- **YN50** — Belge sütunu belgenin varlığını sorar, içeriğini değil; imza sütunu standart
  kitaplığın inceleme aracından alınır.
- **YN51** — Not alma ölçümünde yirmi istek beş ayrı değere düşer; istek dizisi sabittir ve
  yinelenen isteklerin oranı ölçümün kendisidir.
- **YN52** — Sıra ölçümünde iki dekoratör **aynı** dekoratördür ve yalnız etiketleriyle
  ayrılır; iz kaydı giriş ve çıkış noktalarını ayrı yazar, böylece sıra okunabilir.
- **YN53** — Dar imzalı sarmalayıcı kasıtlı olarak yalnız tek konumsal argüman kabul eder;
  ölçülen şey çağrının başarısız olduğu ve hangi istisna sınıfının doğduğudur.

#### Ölçüm

```python
"""Dekorator: sarmalama kac katman kurar ve meta veri korunmazsa ne kaybolur."""

import functools
import inspect

SAYAC = {"uretilen": 0, "dekorator": 0, "sarmalayici": 0, "govde": 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():
    for k in SAYAC:
        SAYAC[k] = 0


def kaba(islev):
    """Meta veriyi korumayan sarmalayici."""
    SAYAC["dekorator"] += 1

    def sarmalayici(*a, **k):
        SAYAC["sarmalayici"] += 1
        return islev(*a, **k)
    return sarmalayici


def korumali(islev):
    """functools.wraps ile meta veriyi tasiyan sarmalayici."""
    SAYAC["dekorator"] += 1

    @functools.wraps(islev)
    def sarmalayici(*a, **k):
        SAYAC["sarmalayici"] += 1
        return islev(*a, **k)
    return sarmalayici


def yineleyen(kere):
    """Parametreli dekorator: once fabrika cagrilir, sonra dekorator."""
    def dekorator(islev):
        SAYAC["dekorator"] += 1

        @functools.wraps(islev)
        def sarmalayici(*a, **k):
            SAYAC["sarmalayici"] += 1
            for _ in range(kere):
                sonuc = islev(*a, **k)
            return sonuc
        return sarmalayici
    return dekorator


def hesapla(n: int):
    """Verilen sayidan bir oge uretir."""
    SAYAC["govde"] += 1
    return Oge(n)


def katman(islev):
    sayi = 1
    while hasattr(islev, "__wrapped__"):
        islev, sayi = islev.__wrapped__, sayi + 1
    return sayi


def cekirdek(islev):
    while hasattr(islev, "__wrapped__"):
        islev = islev.__wrapped__
    return islev


sifirla()
sarmasiz = hesapla
tekli = korumali(hesapla)
ikili = korumali(korumali(hesapla))
parametreli = yineleyen(3)(hesapla)
tanimda = SAYAC["dekorator"]

print(f"{'yazım':<24s} {'katman':>7s} {'çekirdek özgün mü':>18s}"
      f" {'gövde çağrısı':>14s}")
for ad, f in (("sarmasız", sarmasiz), ("tek dekoratör", tekli),
              ("iki dekoratör", ikili), ("parametreli dekoratör", parametreli)):
    SAYAC["govde"] = 0
    f(7)
    print(f"  {ad:<22s} {katman(f):7d}"
          f" {str(cekirdek(f) is hesapla):>18s} {SAYAC['govde']:14d}")

print()
print(f"dört yazım kurulurken çalışan dekoratör gövdesi: {tanimda};"
      f" her biri bir kez çağrılınca çalışan sarmalayıcı:"
      f" {SAYAC['sarmalayici']}")
print(f"tekli is hesapla -> {tekli is hesapla},"
      f" tekli.__wrapped__ is hesapla -> {tekli.__wrapped__ is hesapla}")

print()
print(f"{'yazım':<16s} {'__name__':>12s} {'belge var mı':>13s} {'imza':>18s}")
for ad, f in (("sarmasız", hesapla), ("wraps yok", kaba(hesapla)),
              ("wraps var", korumali(hesapla))):
    print(f"  {ad:<14s} {f.__name__:>12s} {str(bool(f.__doc__)):>13s}"
          f" {str(inspect.signature(f)):>18s}")


@functools.cache
def pahali(n):
    SAYAC["govde"] += 1
    return Oge(n % 5)


sifirla()
istekler = [i % 5 for i in range(20)]
for i in istekler:
    pahali(i)
print()
print(f"not alma: {len(istekler)} istek, gövde çağrısı {SAYAC['govde']},"
      f" var edilen {SAYAC['uretilen']}, önbellekte tutulan"
      f" {pahali.cache_info().currsize}")

IZ = []


def izleyen(etiket):
    def dekorator(islev):
        @functools.wraps(islev)
        def sarmalayici(*a, **k):
            IZ.append(f"{etiket} giriş")
            sonuc = islev(*a, **k)
            IZ.append(f"{etiket} çıkış")
            return sonuc
        return sarmalayici
    return dekorator


@izleyen("dış")
@izleyen("iç")
def ustten(n):
    IZ.append("gövde")
    return Oge(n)


@izleyen("iç")
@izleyen("dış")
def alttan(n):
    IZ.append("gövde")
    return Oge(n)


print()
for ad, f in (("@dış @iç", ustten), ("@iç @dış", alttan)):
    IZ.clear()
    f(1)
    print(f"  {ad:<10s} {' -> '.join(IZ)}")


def dar(islev):
    @functools.wraps(islev)
    def sarmalayici(n):
        return islev(n)
    return sarmalayici


@dar
def iki_argumanli(n, k=2):
    return n * k


try:
    iki_argumanli(3, k=5)
except TypeError as e:
    print(f"dar imzalı sarmalayıcı anahtarlı argümanla: {type(e).__name__}")
print(f"geniş imzalı sarmalayıcı aynı çağrıda:"
      f" {izleyen('x')(lambda n, k=2: n * k)(3, k=5)}")
```

```
yazım                     katman  çekirdek özgün mü  gövde çağrısı
  sarmasız                     1               True              1
  tek dekoratör                2               True              1
  iki dekoratör                3               True              1
  parametreli dekoratör        2               True              3

dört yazım kurulurken çalışan dekoratör gövdesi: 4; her biri bir kez çağrılınca çalışan sarmalayıcı: 4
tekli is hesapla -> False, tekli.__wrapped__ is hesapla -> True

yazım                __name__  belge var mı               imza
  sarmasız            hesapla          True           (n: int)
  wraps yok       sarmalayici         False          (*a, **k)
  wraps var           hesapla          True           (n: int)

not alma: 20 istek, gövde çağrısı 5, var edilen 5, önbellekte tutulan 5

  @dış @iç   dış giriş -> iç giriş -> gövde -> iç çıkış -> dış çıkış
  @iç @dış   iç giriş -> dış giriş -> gövde -> dış çıkış -> iç çıkış
dar imzalı sarmalayıcı anahtarlı argümanla: TypeError
geniş imzalı sarmalayıcı aynı çağrıda: 15
```

#### Katmanların Sayısı

Üst tabloda katman sütunu doğrudan sayıyor: sarmasız işlev **1**, tek dekoratör **2**, iki
dekoratör **3**. Her sarmalama zincire bir halka ekliyor ve halka sayısı dekoratör sayısının
bir fazlası.

İkinci sütun daha önemli olanı söylüyor: her satırda zincirin dibindeki nesne **özgün
işlevin kendisi**. Sarmalama özgün nesneyi ne değiştiriyor ne yok ediyor; onu bir
başvurunun ucunda canlı tutuyor. Alttaki iki satır bunu doğrudan gösteriyor:
`tekli is hesapla` **yanlış** — ad artık başka bir nesneye bağlı — ama
`tekli.__wrapped__ is hesapla` **doğru**. Yeni nesne var edildi, eski nesne **paylaşıldı**.

Kursun ekseninden bakınca sarmalama bir kopyalama değil, bir **kap kurmadır**: dış nesne
yeni, içindeki eski. Birinci konunun dilimleme ölçümüyle aynı örüntü, bu kez işlevler
üzerinde.

Dördüncü satır katmanın **davranış** eklediğini gösteriyor: parametreli dekoratörle
sarmalanmış işlev bir kez çağrıldığında özgün gövde **3** kez çalışıyor. Katman sayısı yine
**2** — parametre bir katman eklemiyor, yalnız katmanın davranışını belirliyor. Parametreli
dekoratör üç aşamalıdır: önce fabrika çağrılır ve dekoratörü üretir, sonra dekoratör çağrılır
ve sarmalayıcıyı üretir, sonra sarmalayıcı çağrılır.

Sayının maliyet tarafı da doğrudan okunur. Her sarmalama bir işlev nesnesi var eder ve
özgün nesneyi canlı tutar; iki dekoratörle sarmalanmış bir işlev için bellekte **3** işlev
nesnesi durur ve her çağrı üç çerçeve açar. Bir işlevi sarmalamak ucuzdur, ama ücretsiz
değildir ve ücret **katman başına** ödenir.

Sonraki satır iki zamanı ayırıyor. Dört yazım kurulurken dekoratör gövdesi **4** kez çalıştı
— sarmalanan her işlev için bir kez, hiçbiri henüz çağrılmadan. Sarmalayıcılar ise ancak
çağrılar yapıldığında çalıştı. Tanım anındaki iş bir kez ödenir; çağrı anındaki iş her
çağrıda.

#### Kaybolan Meta Veri

Orta tablo dersin ikinci sorusunu yanıtlıyor ve orta satır kaybı gösteriyor.

Meta veri koruma kullanılmadığında sarmalanmış işlevin `__name__` özniteliği **sarmalayıcı**
oluyor, belgesi **kayboluyor** ve imzası `(*a, **k)` görünüyor. Üç bilgi de özgün nesnede
duruyor, ama artık kimse ona bakmıyor: ada bağlı olan nesne sarmalayıcıdır ve bu üç
özniteliği kendisininkilerdir.

Kaybın nerede ödendiği önemlidir. Ad hata izlerinde ve günlük kayıtlarında görünür; on ayrı
işlev aynı dekoratörle sarmalanmışsa onunun da adı aynı olur ve iz hangi işlevin
çalıştığını söyleyemez. Belge çevrimiçi yardımda ve belge üretiminde okunur. İmza ise en
sinsi olanıdır: `(*a, **k)` bir imza değil, imzanın yokluğudur — hangi argümanların
beklendiğini inceleyen hiçbir araç doğru yanıtı alamaz.

Alt satır düzeltmeyi gösteriyor. Standart kitaplığın `wraps` aracı bir dekoratördür ve
sarmalayıcıya uygulandığında özgün nesnenin ad, belge ve imza bilgisini ona kopyalar;
ayrıca `__wrapped__` bağını kurar. Üst tablodaki katman sayımı tam olarak bu bağ sayesinde
mümkün oldu — koruma yoksa zincir izlenemez ve sarmalanmış bir işlevin altında ne olduğu
dışarıdan görülemez.

Ayrım tek cümleyle şudur: sarmalayıcı özgün **davranışı** kendiliğinden korur, çünkü onu
çağırır; özgün **kimliğini** korumaz, çünkü o ayrıca taşınmalıdır.

#### Tutmanın Kazandırdığı

Son satır dekoratörün en yaygın kullanımlarından birini ölçüyor ve kursun eksenini tersine
çeviriyor. Yirmi istek yapılıyor, bunlar beş ayrı değere düşüyor; özgün gövde yalnız **5**
kez çalışıyor, var edilen **5** ve önbellekte tutulan **5**.

Sarmasız hâlde yirmi istek yirmi gövde çağrısı ve yirmi nesne demekti. Sarmalayıcı, gördüğü
argümanlar için sonucu **saklayarak** üretimi dörtte bire indiriyor. Programlama Temelleri
kursu not almayı bir teknik olarak kurmuştu; buradaki katkı onun sayısını vermektir — ve
sayı iki sütunda birden okunmalıdır.

Üretim **20**'den **5**'e indi, ama tutulan **0**'dan **5**'e çıktı. Üçüncü ders tembelliğin
tutmayı azalttığını ölçmüştü; bu satır tam tersini yapıyor: tutmayı artırarak üretimi
azaltıyor. İki ölçüm birlikte kursun ekseninin son biçimini veriyor — **var etmek ile tutmak
arasında bir takas vardır ve hangi yönde durulacağı işin kendisine bağlıdır.** Bir kez
kullanılıp atılacak bin öğe için tutmamak doğrudur; yirmi kez sorulacak beş değer için
tutmak doğrudur.

Önbelleğin sınırsız büyümesi bu takasın açık ucudur: tutulan sayı ayrı argüman sayısı kadar
büyür ve kimse onu düşürmez. Standart kitaplık bu yüzden sınırlı boyutlu bir biçim de
sunar; sınır konduğunda tutulan sabitlenir ve gövde çağrısı yeniden artar.

#### Sıra ve İmza

Son dört satır sarmalamanın iki pratik ayrıntısını ölçüyor.

İki dekoratör üst üste yazıldığında **sıra sonucu değiştirir**. `@dış` üstte olduğunda iz
şöyle ilerliyor: dış giriş, iç giriş, gövde, iç çıkış, dış çıkış. Sıra ters çevrildiğinde iz
de tersine dönüyor. Kural şudur: **en alttaki dekoratör önce uygulanır** ve zincirin en içine
düşer; en üstteki en son uygulanır ve en dışta durur. Çağrı sırası bunun tersidir — en dış
katman ilk çalışır, gövdeye en son ulaşılır ve dönüşte katmanlar ters sırayla kapanır.

Ayrım önemsiz değildir. Bir katman erişim denetimi yapıyor, bir başkası günlük tutuyorsa,
hangisinin dışta olduğu reddedilen bir çağrının kaydedilip kaydedilmeyeceğini belirler. İki
yazım aynı görünür; iz farklıdır.

Alttaki iki satır sarmalayıcının imzasının neden geniş yazıldığını gösteriyor. Yalnız tek
konumsal argüman kabul eden bir sarmalayıcı, özgün işlev anahtarlı argüman alsa bile o
çağrıyı geçiremiyor ve `TypeError` doğuyor. Aynı çağrı, argümanları olduğu gibi aktaran
geniş imzalı sarmalayıcıda çalışıyor ve **15** veriyor.

Bunun nedeni sarmalayıcının artık **çağrı yüzeyi** olmasıdır: dışarıdan gelen her argüman
biçimi önce ona uğrar. Özgün işlevin kabul ettiği her biçimi kabul etmiyorsa, kabul
etmediklerini geçiremez. Değişken sayıda argüman toplama biçimi bu yüzden bir üslup tercihi
değil, sarmalamanın gereğidir — ve önceki tablodaki `(*a, **k)` imzası tam da bunun izidir.
Koruma kullanılmadığında dışarıya görünen imza bu geniş biçimdir; koruma kullanıldığında
görünen imza özgün işlevinkidir, ama çağrıyı gerçekten karşılayan yine geniş biçimdir.

#### Özet

- Dekoratör bir işlev alıp yerine geçecek başka bir işlev döndürür; `@` yazımı
  `hesapla = dekorator(hesapla)` bağlamasının kısaltmasıdır.
- Sarmalama özgün nesneyi yok etmez, katman ekler: sarmasız **1**, tek dekoratör **2**, iki
  dekoratör **3** katman. Ada bağlı nesne değişir (`tekli is hesapla` yanlış) ama özgün
  nesne paylaşılır (`tekli.__wrapped__ is hesapla` doğru).
- Dekoratör gövdesi **tanım anında** ve sarmalanan işlev başına bir kez çalışır; sarmalayıcı
  **çağrı anında** ve her çağrıda çalışır. Parametreli dekoratör üç aşamalıdır ve katman
  sayısını artırmaz.
- Meta veri korunmadığında ad `sarmalayici`, belge boş ve imza `(*a, **k)` olur; koruma
  kullanıldığında üçü de özgün değerini alır ve `__wrapped__` bağı kurulur. Sarmalayıcı
  davranışı kendiliğinden korur, kimliği ayrıca taşınmalıdır.
- Not alan sarmalayıcı yirmi isteği **5** gövde çağrısına indirir ve karşılığında **5** öğe
  tutar: üretim düşer, tutma yükselir. Takasın yönü işin kendisine bağlıdır.
- En alttaki dekoratör önce uygulanır ve en içte durur; çağrı sırası tersinedir. Sarmalayıcı
  bir çağrı yüzeyi olduğu için argümanları olduğu gibi aktarmalıdır — dar imzalı bir
  sarmalayıcı, özgün işlevin kabul ettiği bir çağrıda `TypeError` doğurur.

#### Sonraki Adım

Buraya kadar her şey **tek bir dosyanın** içindeydi. Ölçülen her sınıf, her üreteç, her
dekoratör aynı gövdede tanımlandı ve aynı gövdede kullanıldı; bir adın nereye yazıldığı
sorusu hiç sorulmadı, çünkü yazılacak tek bir yer vardı. Oysa bu dersin sarmalayıcısı başka
bir dosyada tanımlanmış bir işlevi de sarmalayabilirdi. Kod birden çok dosyaya yayıldığında
adlar nereye yazılır, bir dosya bir başkasının adlarını nasıl görür ve aynı dosya iki kez
istendiğinde iki kez mi okunur? Sonraki konu bunun düzeneğini kurar.

## Modüller ve Paketler

İçe aktarma mekaniği ve arama yolu, alt paketler ile göreli içe aktarım, standart kitaplığın kapsamı, ve betik ile modül ikiliğinin gövdeyi ne zaman çalıştırdığı.

### Modül Sistemi

Source: https://academia.sh/tr/kurslar/python-veri-yapilari/modul-sistemi

İçe aktarma bir var etme işidir: aynı modül üç kez istendiğinde iki nesne var edilir, gövde önbellek doluyken bir kez koşar; arama yolunun sırası ancak önbellek boşken karar verir.

Buraya kadar her şey **tek bir dosyanın** içindeydi. Kavramalar, üreteçler, sarmalayıcılar
ve dekoratörler aynı ad alanında durdu; bir ad tanımlandığı yerden görünüyordu ve
görünmediğinde nedeni kapsamdı. Kod birden çok dosyaya yayıldığında bu sorunun biçimi
değişir: adlar artık **nereye** yazılır ve bir dosya öbürünün adını **hangi işlemle** alır?

Yanıt bu kursun ölçü ekseniyle verilir. İçe aktarma bir ad kopyalama işlemi değil, bir
**var etme** işidir: bir dosya ilk kez istendiğinde ondan bir **modül (module)** nesnesi var
edilir, gövdesi bir kez koşar ve bütün modül düzeyindeki adlar o nesnenin sözlüğüne yazılır.
İkinci istek yeni bir nesne var etmez. Bu dersin sorusu şudur: aynı modül birden çok kez
istendiğinde **kaç nesne var edilir**, **gövde kaç kez koşar** ve **hangi kayıt** ikinci
koşumu engeller?

#### İçe Aktarma Neyi Var Eder

Bir içe aktarma isteği üç iş yapar. Önce adın **modül önbelleğinde (module cache)** kayıtlı
olup olmadığına bakılır; kayıtlıysa oradaki nesne verilir ve iş biter. Kayıtlı değilse
kaynak **arama yolunda (search path)** aranır, bulunan kaynaktan bir modül nesnesi var
edilir, o nesne **önce önbelleğe yazılır**, sonra gövdesi koşturulur. Son adımda istekte
geçen ad, isteği yapan tarafta modül nesnesine bağlanır.

Sıralamanın ayrıntısı önemlidir: nesne önbelleğe **gövde koşmadan önce** yazılır. Bu yüzden
bir modülün gövdesi kendisini dolaylı olarak isteyen başka bir modülü içe aktarsa bile
sonsuz döngü doğmaz — ikinci istek, gövdesi henüz bitmemiş ama nesnesi var edilmiş modülü
önbellekte bulur.

Bunun doğrudan bir sonucu var: içe aktarma **ucuz değildir, bir kez pahalıdır**. Gövdede ne
varsa — sınıf tanımları, modül düzeyindeki hesaplar, tablolar — hepsi ilk istekte bir kez
koşar ve sonuçları modül nesnesinde tutulur. Sonraki istekler yalnızca bir sözlük araması
kadar iş yapar.

#### İki Yazım, Aynı Nesne

İçe aktarmanın iki yazımı vardır ve ikisi de **aynı modül nesnesini** var eder; ayrıldıkları
yer, isteği yapan tarafta **hangi adın neye bağlandığıdır**. Birinci yazım modül nesnesinin
kendisini bir ada bağlar; ikinci yazım modülün sözlüğünden **tek bir değeri** okur ve o değeri
yerel bir ada bağlar. İkincisi yeni nesne var etmez — var edilmiş bir nesneye ikinci bir ad
verir.

Ayrımın gözlenebilir sonucu şudur: iki ad **nesneyi** paylaşır, **bağı** paylaşmaz. Modülün
sözlüğündeki ad sonradan başka bir nesneye bağlanırsa, dışarıda alınmış ad eski nesnede
kalır — çünkü o ad modülün sözlüğüne değil, kendi sözlüğüne yazılmıştır. Buna karşılık
paylaşılan nesne yerinde değiştirilirse, iki taraf da aynı değişikliği görür. Ölçümün son
bölümü bu iki durumu ayrı ayrı sınıyor.

#### Arama Yolu Bir Listedir

Önbellekte bulunmayan bir ad için sıra arama yoluna gelir. Arama yolu bir dizin adları
listesidir ve **sıralıdır**: ilk eşleşen kaynak kazanır, geri kalanına bakılmaz. İki ayrı
dizinde aynı adı taşıyan iki kaynak varsa, hangisinin içe aktarılacağını dosyaların içeriği
değil listedeki **sıra** belirler.

Sıranın pratik sonucu, arama yolunun **baş tarafındaki** bir dizinde standart kitaplıkla aynı
adı taşıyan bir dosya bulunduğunda ortaya çıkar: istek o dosyayı bulur ve standart kitaplığın
modülü hiç aranmaz. Ortada bir hata iletisi yoktur, çünkü kural doğru işlemiştir — ilk
eşleşen kazanmıştır. Ad çakışmasının belirtisi, beklenen adın modülde bulunamamasıdır; nedeni
ise adın **başka bir kaynaktan** gelmiş olmasıdır.

Bu iki düzenek — önbellek ve arama yolu — birlikte çalışırken hangisinin önce söz sahibi
olduğu ölçülebilir bir sorudur. Arama yolunun sırasını bir modül içe aktarıldıktan sonra
değiştirirsek, sonraki istek yeni sıraya mı uyar yoksa önbellekteki eski nesneyi mi verir?
Ölçüm bunu yanıtlıyor.

#### Modül Düzeyindeki Ad Nereye Yazılır

Kapsam kuralları bu kursun konusu değil; dört düzey — yerel, kapsayan, genel, yerleşik —
**Python Temelleri** kursunun kapsam dersinde kuruldu ve burada tekrarlanmaz. Buraya eklenen
tek şey şudur: o dört düzeyden **genel** olanı soyut bir alan değil, somut bir nesnenin
sözlüğüdür. Modül düzeyinde bağlanan her ad, o modül nesnesinin `__dict__` sözlüğüne yazılır;
`vars` yerleşiği aynı sözlüğü verir. Bir modüle dışarıdan öznitelik atamak da aynı sözlüğe
yazmaktır.

Bu, "genel değişken" deyiminin neden yanıltıcı olduğunu açıklar. Genel olan hiçbir şey
yoktur; **modüle ait** olan vardır. İki modülün aynı adı taşıyan iki değişkeni birbirinden
bağımsızdır, çünkü iki ayrı sözlükte dururlar.

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

- **MP1** — Ölçüm depoya dosya bırakmaz. Modül kaynakları geçici bir dizinde üretilir, iş
  bitince dizin silinir; **dizinin yolu basılmaz.**
- **MP2** — Standart kitaplıktan yapılan içe aktarma gerçek bir içe aktarmadır; kâhin `is`
  karşılaştırmasıdır ve **kimlik sayısı basılmaz.**
- **MP3** — Gövdenin kaç kez koştuğu, gövdenin kendisinin ortak bir listeye eklediği kayıtla
  sayılır. Sayan taraf ölçülen modülün kendisidir.
- **MP4** — Var edilen modül nesnesi sayısı, görülen nesneler bir listede toplanıp `is` ile
  birbirinden ayrılarak bulunur.
- **MP5** — Arama yolu ölçümünde iki ayrı dizin **aynı adı** taşıyan iki kaynak içerir;
  ikisi yalnız modül düzeyindeki bir adın değeriyle ayrılır.
- **MP6** — Sıra değiştirildikten sonra önbelleğe iki kez bakılır: bir kez dolu, bir kez
  boşaltılmış.
- **MP7** — Ölçüm bittiğinde eklenen yol girdileri ve önbellek kayıtları geri alınır;
  yorumlayıcının başlangıç durumu ölçüme karışmaz.
- **MP8** — Adın nereye yazıldığı `vars` ile okunur; `vars` ile `__dict__` sonucunun aynı
  nesne olduğu da `is` ile sınanır.
- **MP9** — İki yazımın karşılaştırmasında ikinci yazım ayrı bir ad alanı sözlüğünde
  koşturulur; ölçüm ana koşumun kendi adlarına dokunmaz.
- **MP10** — Paylaşımın sınanması iki adımlıdır: önce nesne yerinde değiştirilir, sonra
  modüldeki ad başka bir nesneye bağlanır. İki adım ayrı ayrı okunur.

#### Ölçüm

```python
"""Modul sistemi: ice aktarma kac nesne var eder, govde kac kez calisir."""

import importlib
import sys
import tempfile
from pathlib import Path

KAYIT_KAYNAK = "GOVDE = []\n"
OLCUM_KAYNAK = ("import kayit\n"
                "kayit.GOVDE.append('kosum')\n"
                "ETIKET = 'olcum'\n")
SECIM_KAYNAK = "KAYNAK = '{ad}'\n"
ADLAR = ("kayit", "olcum", "secim")


def farkli_nesne(nesneler):
    """Kimlik basmadan, kac ayri nesne goruldugunu sayar."""
    farkli = []
    for n in nesneler:
        if not any(n is f for f in farkli):
            farkli.append(n)
    return len(farkli)


def onbellegi_bosalt():
    for ad in ADLAR:
        sys.modules.pop(ad, None)


def iki_kez_ice_aktar():
    import json as birinci
    import json as ikinci
    return birinci is ikinci, "json" in sys.modules


def govde_kac_kez(kok):
    (kok / "kayit.py").write_text(KAYIT_KAYNAK, encoding="utf-8")
    (kok / "olcum.py").write_text(OLCUM_KAYNAK, encoding="utf-8")
    sys.path.insert(0, str(kok))
    importlib.invalidate_caches()
    gorulen = [importlib.import_module("olcum")]
    gorulen.append(importlib.import_module("olcum"))
    kayit = sys.modules["kayit"]
    onbellekli = len(kayit.GOVDE)
    sys.modules.pop("olcum")
    gorulen.append(importlib.import_module("olcum"))
    sonuc = (len(gorulen), farkli_nesne(gorulen), onbellekli,
             len(kayit.GOVDE), gorulen[-1].kayit is kayit)
    sys.path.remove(str(kok))
    onbellegi_bosalt()
    return sonuc


def arama_yolu(kok):
    for ad in ("bir", "iki"):
        (kok / ad).mkdir()
        (kok / ad / "secim.py").write_text(SECIM_KAYNAK.format(ad=ad),
                                           encoding="utf-8")
    bir, iki = str(kok / "bir"), str(kok / "iki")
    sys.path[:0] = [bir, iki]
    importlib.invalidate_caches()
    once = importlib.import_module("secim").KAYNAK
    sys.path.remove(bir)
    sys.path.insert(1, bir)
    onbellekle = importlib.import_module("secim").KAYNAK
    sys.modules.pop("secim")
    onbelleksiz = importlib.import_module("secim").KAYNAK
    sys.path.remove(bir)
    sys.path.remove(iki)
    onbellegi_bosalt()
    return once, onbellekle, onbelleksiz


def modul_duzeyindeki_ad(kok):
    (kok / "secim.py").write_text(SECIM_KAYNAK.format(ad="bir"),
                                  encoding="utf-8")
    sys.path.insert(0, str(kok))
    importlib.invalidate_caches()
    m = importlib.import_module("secim")
    ayni_sozluk = vars(m) is m.__dict__
    icinde = "KAYNAK" in vars(m)
    m.SONRADAN = "eklendi"
    disaridan = vars(m)["SONRADAN"] == "eklendi"
    sys.path.remove(str(kok))
    onbellegi_bosalt()
    return ayni_sozluk, icinde, disaridan


def iki_yazim(kok):
    (kok / "secim.py").write_text("KAYNAK = ['bir']\n", encoding="utf-8")
    sys.path.insert(0, str(kok))
    importlib.invalidate_caches()
    m = importlib.import_module("secim")
    ad_alani = {}
    exec("from secim import KAYNAK", ad_alani)
    paylasiyor = ad_alani["KAYNAK"] is m.KAYNAK
    m.KAYNAK.append("degisti")
    degisimi_gordu = "degisti" in ad_alani["KAYNAK"]
    m.KAYNAK = ["yeni"]
    yeniden_baglamayi_gordu = ad_alani["KAYNAK"] is m.KAYNAK
    sys.path.remove(str(kok))
    onbellegi_bosalt()
    return paylasiyor, degisimi_gordu, yeniden_baglamayi_gordu


ayni, kayitli = iki_kez_ice_aktar()
print(f"iki kez içe aktarılan modül aynı nesne -> {ayni}, "
      f"önbellekte kayıtlı -> {kayitli}")

with tempfile.TemporaryDirectory() as d:
    istek, nesne, onbellekli, temizden, paylasilan = govde_kac_kez(Path(d))
print()
print(f"içe aktarma isteği {istek} | var edilen modül nesnesi {nesne} | "
      f"gövde koşumu önbellekliyken {onbellekli}, önbellek silinince {temizden}")
print(f"iki modülün gördüğü kayıt modülü aynı nesne -> {paylasilan}")

with tempfile.TemporaryDirectory() as d:
    once, onbellekle, onbelleksiz = arama_yolu(Path(d))
print()
print(f"arama yolu sırası bir-iki -> {once}")
print(f"sıra iki-bir, önbellek dolu -> {onbellekle}")
print(f"sıra iki-bir, önbellek silinmiş -> {onbelleksiz}")

with tempfile.TemporaryDirectory() as d:
    ayni_sozluk, icinde, disaridan = modul_duzeyindeki_ad(Path(d))
print()
print(f"vars(m) is m.__dict__ -> {ayni_sozluk} | modül düzeyindeki ad "
      f"sözlükte -> {icinde} | dışarıdan yazılan ad aynı sözlükte -> {disaridan}")

with tempfile.TemporaryDirectory() as d:
    paylasiyor, degisimi, yeniden = iki_yazim(Path(d))
print()
print(f"içeri alınan ad modüldekiyle aynı nesne -> {paylasiyor} | "
      f"nesnenin değişimini gördü -> {degisimi} | "
      f"yeniden bağlamayı gördü -> {yeniden}")
```

```
iki kez içe aktarılan modül aynı nesne -> True, önbellekte kayıtlı -> True

içe aktarma isteği 3 | var edilen modül nesnesi 2 | gövde koşumu önbellekliyken 1, önbellek silinince 2
iki modülün gördüğü kayıt modülü aynı nesne -> True

arama yolu sırası bir-iki -> bir
sıra iki-bir, önbellek dolu -> bir
sıra iki-bir, önbellek silinmiş -> iki

vars(m) is m.__dict__ -> True | modül düzeyindeki ad sözlükte -> True | dışarıdan yazılan ad aynı sözlükte -> True

içeri alınan ad modüldekiyle aynı nesne -> True | nesnenin değişimini gördü -> True | yeniden bağlamayı gördü -> False
```

#### Üç İstek, İki Nesne, İki Koşum

İlk satır ortak ölçümün beşinci okumasıdır: aynı modül iki kez istendiğinde iki ad **aynı
nesneye** bağlanır ve o nesne önbellekte kayıtlıdır. Var edilen modül nesnesi **1**, tutulan
**1**, iki adın paylaştığı **1**.

İkinci bölüm bunun bedelini gösteriyor. Üç içe aktarma isteği yapıldı; var edilen modül
nesnesi **2** oldu. İlk iki istek arasında önbellek dolu olduğu için gövde koşumu **1**'de
kaldı — ikinci istek yeni nesne var etmedi, gövdeyi yeniden koşturmadı, yalnız önbellekteki
nesneyi verdi. Üçüncü istekten önce kayıt önbellekten silindi; o istek kaynağı yeniden buldu,
**yeni bir modül nesnesi var etti** ve gövdeyi ikinci kez koşturdu. Gövde koşumu **2**'ye
çıktı.

Sayılan üç şey burada birbirinden ayrılıyor. **Var edilen** modül nesnesi 2, **tutulan** —
yani ölçümün sonunda önbellekte kayıtlı olan — 1 ve **paylaşılan** olan `kayit` modülü.
Son satır bunu söylüyor: ölçüm modülünün gövdesinde içe aktarılan `kayit` ile ana koşumun
önbellekten aldığı `kayit` **aynı nesnedir**. Ölçüm modülü iki ayrı kez var edildi ama
ikisinin de gördüğü kayıt tektir, çünkü onun kaydı hiç silinmedi.

Buradan çıkan kural şudur: **modül nesnesinin sayısı dosya sayısına değil, önbellek
kaydının kaç kez düştüğüne bağlıdır.** Aynı dosyadan iki modül nesnesi var edilebilir ve o
iki nesnenin sözlükleri birbirinden bağımsızdır — birinde tanımlanan sınıf, öbüründe
tanımlanan sınıftan ayrı bir nesnedir. İki nesne aynı kaynaktan üretildiği için aynı adları
taşır; taşıdıkları adların bağlandığı nesneler ise ortak değildir. Bir kimlik sınaması bu
durumda beklenmedik bir sonuç verir: aynı dosyadan gelmiş iki sınıf, aynı ada sahip olduğu
hâlde birbirinin yerine geçmez.

#### Sıra mı, Önbellek mi

Üçüncü bölüm iki düzeneği karşı karşıya getiriyor. Arama yolunun başında `bir` dizini
varken içe aktarma **bir** veriyor, beklendiği gibi. Sonra sıra değiştirildi: `iki` başa
alındı. Önbellek dolu olduğu için sonuç değişmedi — hâlâ **bir**. Önbellek kaydı silindikten
sonra aynı istek **iki** verdi.

Bu, önbelleğin arama yolundan **önce** geldiğinin doğrudan kanıtıdır. Arama yolu yalnız
önbellekte kayıt yokken karar verir; kayıt varsa yol hiç okunmaz. Yolu çalışma sırasında
değiştirmenin, o ana kadar içe aktarılmış hiçbir modüle etkisi olmaz.

Dördüncü bölüm adın nereye yazıldığını kapatıyor. `vars` ile `__dict__` **aynı sözlüğü**
veriyor, modül düzeyinde bağlanan ad orada duruyor ve modüle dışarıdan atanan öznitelik de
**aynı sözlüğe** yazılıyor. Modülün ad alanı ile modülün özniteliklerinin ayrı iki şey
olmadığı buradan görünür: dosyanın içinde `KAYNAK = "bir"` yazmakla, dışarıdan
`m.KAYNAK = "bir"` yazmak aynı sözlükte aynı anahtarı bağlar.

#### Nesne Paylaşılır, Bağ Paylaşılmaz

Son bölüm iki yazımın farkını üç değerle veriyor. İçeri alınan ad ile modüldeki ad **aynı
nesneye** bağlı: paylaşılan nesne **1**, var edilen yeni nesne **0**. İkinci yazım hiçbir şey
var etmedi, var edilmiş bir nesneye ikinci bir ad verdi.

İkinci değer paylaşımın canlı olduğunu gösteriyor. Modüldeki nesne **yerinde** değiştirilince
içeri alınan ad değişikliği **gördü** — aynı nesneye baktıkları için başka türlüsü olamazdı.
Üçüncü değer sınırı çiziyor: modüldeki ad başka bir nesneye **yeniden bağlanınca** içeri
alınan ad bunu **görmedi**. O ad artık eski nesnede duruyor ve modülün sözlüğüyle bağı yok.

Ayrımı bir cümleye indirgemek gerekirse: **içe aktarma nesneyi paylaşır, adı kopyalar.** Bir
modülün sonradan değişecek bir adını izlemek gerekiyorsa modülün kendisi içeri alınır ve
erişim her seferinde modül üzerinden yapılır; değişmeyecek bir nesne için ikinci yazım
fazladan bir arama kadar iş kazandırır. İki durum arasındaki fark bir üslup tercihi değil,
ölçülen bir davranış farkıdır.

#### Özet

- İçe aktarma bir ad kopyalama değil **var etme** işidir: kaynak bir kez okunur, ondan bir
  modül nesnesi var edilir ve gövde bir kez koşar.
- Sıra önbellek, arama yolu, gövde biçimindedir; nesne önbelleğe **gövde koşmadan önce**
  yazılır ve iki kez içe aktarılan modül **aynı nesnedir**.
- Üç istek, önbellek bir kez silindiğinde **2** modül nesnesi var eder ve gövdeyi **2** kez
  koşturur; önbellek bozulmasaydı ikisi de **1** kalırdı.
- Arama yolunun sırası ancak önbellekte kayıt yokken karar verir: sıra değiştirilip önbellek
  dolu bırakıldığında sonuç değişmez, kayıt silinince değişir.
- Modül düzeyindeki her ad, modül nesnesinin `__dict__` sözlüğüne yazılır; `vars` aynı
  sözlüğü verir ve dışarıdan atanan öznitelik de oraya gider.
- Modülden tek bir ad almak **0** yeni nesne var eder ve nesneyi paylaşır; nesnenin yerinde
  değişimi görünür, modüldeki adın yeniden bağlanması görünmez.

#### Sonraki Adım

Ölçümde modüller düz durdu: hepsi arama yolundaki bir dizinin doğrudan içindeydi ve adları
tek parçaydı. Kod büyüdüğünde adlar noktayla bölünür ve dizinler iç içe girer. O zaman aynı
kısa ad birden çok yerde bulunabilir — bir üst dizinde bir tane, bir alt dizinde bir tane
daha. Sonraki ders bu durumu ölçüyor: aynı kısa ad, biri komşuluğa biri kökten yazılmış iki
istekle çağrıldığında **hangi nesneye** çözülür ve iç içe dizinlerin kaç gövdesi koşar?

### Paket Yapısı

Source: https://academia.sh/tr/kurslar/python-veri-yapilari/paket-yapisi

Aynı kısa ad göreli yazımda komşuya, mutlak yazımda köke çözülür ve iki ayrı nesne verir; tek bir alt modül isteği beş gövde koşturur ve beş modül nesnesi önbelleğe yazar.

Önceki ders modülleri düz bir listede aradı: her kaynak, arama yolundaki bir dizinin
doğrudan içindeydi ve adları tek parçaydı. O düzende iki dosyanın aynı adı taşıması bir
çakışmaydı ve kazananı yalnızca sıra belirliyordu. Kod büyüdüğünde bu düzen yetmez; aynı işi
yapan dosyalar birbirine yakın durmak, aynı kısa adı **farklı bağlamlarda** taşımak ister.

**Paket (package)**, dizinlerin ad alanına katıldığı düzendir: bir dizin ve içindeki
`__init__.py` dosyası birlikte bir modül nesnesi verir ve o dizinin içindekiler noktayla
ona bağlanır. Bu, adı iki parçalı yapar — bir yolu ve bir kısa adı olur. Dersin sorusu şudur:
aynı kısa ad iki ayrı düzeyde varken, **göreli** ve **mutlak** yazım hangi nesneye çözülür
ve tek bir istek kaç gövde koşturur?

#### Dizin Nasıl Modül Olur

Bir paket, içinde `__init__.py` bulunan dizindir. O dosya paketin **gövdesidir** ve paket ilk
kez istendiğinde bir kez koşar; boş olması da bir seçenektir, o zaman gövde hiçbir ad
bağlamaz ama modül nesnesi yine var edilir. Paketin içindeki dosyalar ve alt dizinler onun
**alt modülleri** ve **alt paketleridir**.

Noktalı bir ad soldan sağa çözülür ve her düzey ayrı bir modül nesnesidir. `paket.alt.arac`
isteği tek bir nesne değil, üç nesne gerektirir: `paket`, `paket.alt` ve `paket.alt.arac`.
Üçü de ayrı ayrı önbelleğe yazılır, üçünün gövdesi de sırayla koşar. Bu, önceki dersin
kuralının doğrudan sonucudur — her ad kendi kaydını alır.

Çözülen her düzey, bir üstünde **öznitelik olarak da bağlanır**. `paket.alt` içe
aktarıldıktan sonra `paket` modül nesnesinin sözlüğünde `alt` adı vardır ve o ad,
önbellekteki `paket.alt` nesnesinin **aynısına** bağlıdır. Noktalı erişimin çalışma biçimi
budur: nokta bir arama yolu araması değil, bir öznitelik okumasıdır.

#### İki Yazım, İki Başlangıç Noktası

Aynı alt modüle iki yoldan ulaşılır. **Mutlak** yazım adı **kökten** başlatır: adın ilk
parçası arama yolunda aranır ve zincir oradan çözülür. **Göreli içe aktarım (relative
import)** ise adı **isteği yapan modülün paketinden** başlatır; baştaki nokta "bulunduğum
paket", iki nokta "bir üstteki paket" demektir.

İkisi aynı harfleri taşıyan iki ayrı adres üretir. Bir paketin kökünde `arac` adlı bir modül,
alt paketinde de `arac` adlı başka bir modül varsa, alt paketin içinden yazılan göreli istek
**komşudakini**, mutlak istek **kökteki** modülü verir. Adlar aynıdır; çözüldükleri nesneler
ayrıdır.

Göreli içe aktarımın bir koşulu vardır: isteği yapan modülün bir pakete **ait** olması. Bir
paketin içinde olmayan bir modül için "bulunduğum paket" diye bir şey yoktur ve istek
çözülemez. Ölçüm bu durumu da sınıyor.

#### Paketin Gövdesi Kimin Faturası

`__init__.py` bir paketin kapısıdır ve pakete ait **herhangi** bir modül istendiğinde koşar.
Bu, oraya yazılanın bedelini paketin bütün kullanıcılarına ödettiği anlamına gelir. İki
yaygın yazım vardır ve aralarındaki fark ölçülebilir.

**Sade** yazımda `__init__.py` yalnız paketin kendi adlarını bağlar, alt modülleri içe
aktarmaz; kullanıcı hangi alt modülü istiyorsa onu ayrıca yazar. **Peşin** yazımda
`__init__.py` alt modülleri kendisi içe aktarır ve onların adlarını paket düzeyine taşır;
kullanıcı tek bir addan hepsine ulaşır. İkincisi kısa yazım kazandırır ama tek bir yaprağı
isteyen kullanıcıya **bütün** kardeşlerin gövdesini koşturur. Ölçümün son bölümü aynı tek
yaprak isteğini iki yazımda karşılaştırıyor.

#### Bir İstek Kaç Gövde Koşturur

İçe aktarmanın bir var etme işi olduğu önceki derste ölçüldü. Paketlerde bu sayı **zincirin
uzunluğuyla** büyür ve zincirin bir tarafı da içe aktarılan modülün kendi isteklerinden
gelir. Bir alt modül, gövdesinde başka modüller içe aktarıyorsa onlar da aynı istekte koşar.
Ölçüm, tek bir isteğin koşturduğu gövdeleri **koşum sırasıyla** yazıyor.

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

- **MP11** — Ölçüm depoya dosya bırakmaz; paket ağacı geçici bir dizinde üretilir, iş bitince
  silinir ve **dizinin yolu basılmaz.**
- **MP12** — Koşan gövdeler, her gövdenin ortak bir listeye kendi adını eklemesiyle sayılır;
  sayan taraf koşan modülün kendisidir ve liste **koşum sırasını** korur.
- **MP13** — Kök ve alt pakette **aynı kısa adı** taşıyan iki modül vardır; ikisi yalnız
  modül düzeyindeki bir adın değeriyle ayrılır.
- **MP14** — Göreli ve mutlak istek, ölçülen üçüncü bir modülün gövdesinden yapılır; iki
  sonuç o modülün sözlüğünden okunur.
- **MP15** — Hangi adın bağlandığı ayrı bir ad alanı sözlüğünde ölçülür; iki tanımlı ad
  dışındaki adlar ayıklanır.
- **MP16** — Pakete ait olmayan bir modülde göreli istek denenir; kâhin istisnanın **sınıf
  adıdır**, ileti metni değil.
- **MP17** — Ölçüm bittiğinde eklenen yol girdisi ve bütün önbellek kayıtları geri alınır.
- **MP18** — Sade ve peşin yazım **aynı ağaç biçimini** taşır: bir paket ve üç yaprak. Tek
  fark, peşin yazımın `__init__.py` dosyasındaki tek satırdır.
- **MP19** — İki yazımın karşılaştırmasında istenen **tek bir yapraktır** ve sayım her
  ölçümden önce sıfırlanır; iki ölçüm birbirinin sayısını taşımaz.

#### Ölçüm

```python
"""Paket yapisi: ayni kisa ad iki yazimda hangi nesneye cozulur."""

import importlib
import sys
import tempfile
from pathlib import Path


def govde(ad, ek=""):
    return f"import kayit\nkayit.GOVDE.append('{ad}')\n{ek}"


KAYNAKLAR = {
    "kayit.py": "GOVDE = []\n",
    "paket/__init__.py": govde("paket"),
    "paket/arac.py": govde("paket.arac", "AD = 'paket.arac'\n"),
    "paket/alt/__init__.py": govde("paket.alt"),
    "paket/alt/arac.py": govde("paket.alt.arac", "AD = 'paket.alt.arac'\n"),
    "paket/alt/kullanan.py": govde("paket.alt.kullanan",
                                   "from . import arac as goreli\n"
                                   "from paket import arac as mutlak\n"),
    "yalniz.py": "from . import arac\n",
    "sade/__init__.py": govde("sade"),
    "pesin/__init__.py": govde("pesin", "from . import x, y, z\n"),
}
for kok_ad in ("sade", "pesin"):
    for yaprak in ("x", "y", "z"):
        KAYNAKLAR[f"{kok_ad}/{yaprak}.py"] = govde(f"{kok_ad}.{yaprak}")

TEPELER = ("kayit", "yalniz", "paket", "sade", "pesin")


def kur(kok):
    for ad, metin in KAYNAKLAR.items():
        yol = kok / ad
        yol.parent.mkdir(parents=True, exist_ok=True)
        yol.write_text(metin, encoding="utf-8")
    sys.path.insert(0, str(kok))
    importlib.invalidate_caches()


def sok(kok):
    sys.path.remove(str(kok))
    for ad in list(sys.modules):
        if ad.split(".")[0] in TEPELER:
            sys.modules.pop(ad)


def olc(kok):
    kur(kok)
    kullanan = importlib.import_module("paket.alt.kullanan")
    kayit = sys.modules["kayit"]
    kosan = list(kayit.GOVDE)
    kayitli = sorted(a for a in sys.modules if a.split(".")[0] == "paket")
    goreli, mutlak = kullanan.goreli, kullanan.mutlak
    oznitelik = (sys.modules["paket"].alt is sys.modules["paket.alt"]
                 and sys.modules["paket.alt"].arac
                 is sys.modules["paket.alt.arac"])
    importlib.import_module("paket.alt.arac")
    ikinci_istek = len(kayit.GOVDE) - len(kosan)

    ns_nokta, ns_from = {}, {}
    exec("import paket.arac", ns_nokta)
    exec("from paket import arac", ns_from)
    bagli_nokta = sorted(a for a in ns_nokta if not a.startswith("__"))
    bagli_from = sorted(a for a in ns_from if not a.startswith("__"))
    ayni_nesne = ns_nokta["paket"].arac is ns_from["arac"]

    try:
        importlib.import_module("yalniz")
        hata = "yok"
    except Exception as e:
        hata = type(e).__name__

    kayit.GOVDE.clear()
    importlib.import_module("sade.x")
    sade = list(kayit.GOVDE)
    kayit.GOVDE.clear()
    importlib.import_module("pesin.x")
    pesin = list(kayit.GOVDE)

    sok(kok)
    return {"kosan": kosan, "kayitli": kayitli, "ikinci": ikinci_istek,
            "ayni": goreli is mutlak,
            "goreli_ad": goreli.AD, "mutlak_ad": mutlak.AD,
            "oznitelik": oznitelik, "bagli_nokta": bagli_nokta,
            "bagli_from": bagli_from, "ayni_nesne": ayni_nesne, "hata": hata,
            "sade": sade, "pesin": pesin}


with tempfile.TemporaryDirectory() as d:
    s = olc(Path(d))

print(f"tek istek {len(s['kosan'])} gövde koşturdu: {' -> '.join(s['kosan'])}")
print(f"önbelleğe yazılan paket modülü {len(s['kayitli'])}: {s['kayitli']}")
print(f"aynı alt modül ikinci kez istendiğinde koşan gövde {s['ikinci']}")
print()
print(f"göreli yazım    -> {s['goreli_ad']}")
print(f"mutlak yazım    -> {s['mutlak_ad']}")
print(f"aynı nesne mi   -> {s['ayni']}")
print()
print(f"noktalı yazımın bağladığı ad {s['bagli_nokta']}, "
      f"from yazımının bağladığı ad {s['bagli_from']}")
print(f"ikisinin ulaştığı modül aynı nesne -> {s['ayni_nesne']}")
print(f"alt paket üst pakette öznitelik olarak bağlı -> {s['oznitelik']}")
print(f"paketsiz modülde göreli içe aktarım -> {s['hata']}")
print()
print(f"{'tek yaprak isteği':<22s} {'koşan gövde':>12s}  koşum sırası")
print(f"{'sade paket':<22s} {len(s['sade']):12d}  {' -> '.join(s['sade'])}")
print(f"{'peşin içe aktaran':<22s} {len(s['pesin']):12d}  "
      f"{' -> '.join(s['pesin'])}")
```

```
tek istek 5 gövde koşturdu: paket -> paket.alt -> paket.alt.kullanan -> paket.alt.arac -> paket.arac
önbelleğe yazılan paket modülü 5: ['paket', 'paket.alt', 'paket.alt.arac', 'paket.alt.kullanan', 'paket.arac']
aynı alt modül ikinci kez istendiğinde koşan gövde 0

göreli yazım    -> paket.alt.arac
mutlak yazım    -> paket.arac
aynı nesne mi   -> False

noktalı yazımın bağladığı ad ['paket'], from yazımının bağladığı ad ['arac']
ikisinin ulaştığı modül aynı nesne -> True
alt paket üst pakette öznitelik olarak bağlı -> True
paketsiz modülde göreli içe aktarım -> ImportError

tek yaprak isteği       koşan gövde  koşum sırası
sade paket                        2  sade -> sade.x
peşin içe aktaran                 4  pesin -> pesin.x -> pesin.y -> pesin.z
```

#### Beş Gövde, Beş Nesne

İlk satır tek bir isteğin bedelini sırasıyla veriyor. `paket.alt.kullanan` istendi; koşan
gövde **5** oldu. Sıra kendini açıklıyor: önce `paket`, sonra `paket.alt` — noktalı ad soldan
sağa çözülüyor ve her düzeyin gövdesi kendi sırasında koşuyor. Üçüncü sırada istenen modülün
kendi gövdesi başlıyor ve o gövde iki istek daha yapıyor: göreli istek `paket.alt.arac`'ı,
mutlak istek `paket.arac`'ı koşturuyor.

Var edilen modül nesnesi **5**, tutulan **5** — hepsi önbellekte kayıtlı kalıyor. Üçüncü
satır bunun sonucunu ödüyor: aynı alt modül ikinci kez istendiğinde koşan gövde **0**. İkinci
istek yeni nesne var etmiyor, hiçbir gövdeyi koşturmuyor, yalnız önbellekteki kaydı veriyor.

Buradan çıkan ölçü şudur: **bir paket isteğinin maliyeti istenen modülde bitmez.** Zincirdeki
her düzey ve o düzeylerin gövdelerinde yapılan her istek aynı faturaya girer. Bir paketin
`__init__.py` dosyasının içinde ne varsa, o pakete ait **herhangi** bir modülü isteyen herkes
onu ödemiş olur.

#### Aynı Ad, İki Nesne

Orta bölüm dersin ana ölçüsüdür. `paket/alt/kullanan.py` içinde aynı kısa ad iki kez
yazılıyor; göreli yazım **`paket.alt.arac`**, mutlak yazım **`paket.arac`** veriyor ve
`is` karşılaştırması **False**. İki ad, iki ayrı nesne. Var edilen **2**, paylaşılan **0**.

Ayrım tamamen **başlangıç noktasındadır**. Göreli yazım adı isteği yapan modülün paketinden
başlatır; `kullanan` modülü `paket.alt` içinde durduğu için komşusunu bulur. Mutlak yazım
adı arama yolundan başlatır ve kökteki paketi bulur. Kaynak dosyalarda yazan kısa ad
ikisinde de aynıdır; ayrımı yapan yalnız yazımın kendisidir.

Bunun bir de tersi vardır ve önceki dersin ölçtüğü kuraldan çıkar: önbellek kaydı **ada
göre** tutulur, dosyaya göre değil. Aynı kaynak iki ayrı adla istenirse iki ayrı kayıt açılır
ve iki ayrı modül nesnesi var edilir — önceki derste önbellek kaydı silindiğinde ölçülen
durumun aynısı. O iki nesnenin sözlükleri bağımsız olduğu için içlerinde tanımlanan sınıflar
da ayrı nesnelerdir; birinden üretilmiş bir örnek, öbürünün sınıfına göre sınandığında
beklenen yanıtı vermez. Aynı ağaca hem paket adıyla hem de doğrudan erişilebilmesi bu yüzden
bir kolaylık değil, ikiye bölünmüş bir ad alanıdır.

Bunun okuma tarafında bir sonucu var: bir dosyada yalnız kısa adı görmek **hangi modülün**
kastedildiğini söylemez. Aynı satırın anlamı, dosyanın hangi pakette durduğuna bağlıdır.
Dosya bir başka pakete taşındığında göreli istek sessizce başka bir nesneye çözülür ya da hiç
çözülmez; mutlak istek taşınmadan etkilenmez. İki yazım arasındaki seçim bir üslup tercihi
değil, **adın hangi bağlama bağlandığı** kararıdır.

#### Hangi Ad Bağlanır

Son bölüm iki yazımın ad bağlama farkını veriyor. Noktalı yazım, isteği yapan tarafta
yalnız **`paket`** adını bağlıyor — alt modülün adını değil. Alt modüle o addan noktayla
ulaşılır, çünkü zincirin her düzeyi bir üstüne öznitelik olarak bağlanmıştır. `from` yazımı
ise yalnız **`arac`** adını bağlıyor; paketin adı isteği yapan tarafta hiç görünmüyor.

İki yazım farklı adlar bağlasa da aynı nesneye ulaşıyor: `is` karşılaştırması **True**. Var
edilen yeni nesne **0**; ikisi de önbellekteki tek kaydı paylaşıyor. Bir sonraki satır bunun
paketler tarafını gösteriyor — üst paketin sözlüğündeki `alt` adı ile önbellekteki
`paket.alt` kaydı **aynı nesnedir** ve aynısı bir düzey aşağıda da geçerlidir.

Sonraki satır göreli yazımın sınırıdır. Bir pakete ait olmayan modülde göreli istek
**`ImportError`** ile sonuçlanıyor. Neden açıktır: göreli ad, isteği yapan modülün paketinden
başlar; paketi olmayan bir modül için başlanacak yer yoktur. Bu, bir dosyanın **nasıl
koşturulduğuna** bağlı olarak ortaya çıkabilen bir durumdur ve bu konunun son dersinde
ölçülür.

#### Peşin İçe Aktarmanın Bedeli

Son tablo aynı isteği iki paket yazımında karşılaştırıyor ve fark tek bir satırdan doğuyor.
Sade pakette tek bir yaprak istendiğinde koşan gövde **2**: paketin kendisi ve istenen yaprak.
Peşin yazımda aynı istek **4** gövde koşturuyor — paketin gövdesi ve onun kendi içinden
istediği **üç** yaprağın hepsi. İstenen yaprak zaten o üçün içindedir; sıraya bakıldığında
`pesin.x` paketin gövdesi sırasında koşmuş, dış istek ona sıra geldiğinde önbellekte hazır
bulmuştur.

Sayılar oran olarak okunur: kullanıcı **bir** yaprak istedi, **üç** yaprağın bedelini ödedi.
Yaprak sayısı arttıkça bu oran büyür; on yapraklı bir pakette tek bir yaprak isteği on birin
tamamını koşturur. Var edilen modül nesnesi sade yazımda **2**, peşin yazımda **4**; tutulan
da aynı, çünkü hepsi önbellekte kalır.

Bu bir kusur değil, bir **takas**tır ve iki tarafı da ölçülebilir. Peşin yazım kullanıcıya
kısa bir ad verir ve paketi tek bir yüzey gibi gösterir; bedeli, paketin herhangi bir
parçasını isteyen herkese bütün parçaların gövdesini ödetmektir. Sade yazım bedeli isteyene
bırakır; bedeli, kullanıcının hangi alt modülde ne olduğunu bilmesini gerektirmesidir.
Seçimi belirleyen şey, paketin parçalarının **birlikte mi ayrı mı** kullanıldığıdır.

#### Özet

- Paket, `__init__.py` dosyasıyla birlikte bir modül nesnesi veren dizindir; noktalı ad
  soldan sağa çözülür, **her düzey ayrı bir nesnedir** ve her düzey bir üstüne öznitelik
  olarak bağlanır.
- Tek bir alt modül isteği **5** gövde koşturdu ve **5** modül nesnesi önbelleğe yazdı;
  aynı modülün ikinci isteği **0** gövde koşturdu.
- Aynı kısa ad göreli yazımda `paket.alt.arac`, mutlak yazımda `paket.arac` verdi ve `is`
  **False** çıktı; ayrımı yapan tek şey adın **nereden başlatıldığıdır**.
- Noktalı yazım yalnız kök paketin adını, `from` yazımı yalnız alınan adı bağlar; ikisi de
  **0** yeni nesne var eder ve aynı modül nesnesini paylaşır.
- `__init__.py` alt modülleri peşin içe aktardığında tek bir yaprak isteği **2** yerine
  **4** gövde koşturur; kullanıcı bir yaprak ister, üçünün bedelini öder.
- Pakete ait olmayan bir modülde göreli içe aktarım `ImportError` verir, çünkü adın
  başlayacağı bir paket yoktur.

#### Sonraki Adım

Buraya kadar ölçülen paketlerin hepsi ölçüm için üretildi: gövdeleri bir satırlık, işleri
kendilerini saydırmaktı. Oysa her koşumda hazır bulunan, hiçbir kurulum gerektirmeyen büyük
bir paket kümesi vardır ve dilin kendisiyle birlikte gelir. Sonraki ders o kümenin kapsamına
bakıyor ve tek bir soruyla ölçüyor: aynı işi standart kitaplıktan bir modülle yazmakla elle
yazmak arasında **kaç nesne** ve **kaç satır** fark var ve elle yazım hangi durumlarda
sessizce ayrışıyor?

### Standart Kütüphane Turu

Source: https://academia.sh/tr/kurslar/python-veri-yapilari/standart-kutuphane-turu

Aynı işin standart kitaplıkla ve elle yazımı üç eksende ölçülür: akışta satır aynı kalır ama var edilen nesne 800'e karşı 3'tür, ad ayırmada beş addan 1'i, gün eklemede altı tarihten 1'i sessizce ayrışır.

Önceki derste ölçülen paketlerin hepsi ölçüm için üretildi: gövdeleri bir satırlıktı ve tek
işleri kendilerini saydırmaktı. Oysa her koşumda hazır bulunan, hiçbir kurulum gerektirmeyen
büyük bir modül ve paket kümesi vardır; dilin kendisiyle birlikte gelir ve arama yolunda
zaten durur. Bu ders o kümenin kapsamına bakıyor.

Soru "hangi modül ne yapar" değil — bir başvuru listesi bu dersin işi değildir. Soru şudur:
aynı iş **standart kitaplıktan bir modülle** ve **elle** yazıldığında ne fark eder? Ölçülen
üç eksen var: kaç nesne **var edilir**, kaç nesne **tutulur** ve kaç **satır** yazılır.
Dördüncü bir şey de sayılır: elle yazımın kaç durumda **sessizce** ayrıştığı.

#### Kitaplığın Kapsamı

Standart kitaplık dilin parçasıdır ve dışarıdan bir şey getirmeden içe aktarılır. Kapsadığı
alanlar birkaç öbekte toplanır.

**Metin ve veri biçimleri:** `re` örüntü eşlemeyi, `textwrap` metin sarmayı,
`unicodedata` karakter özelliklerini verir; `json`, `csv` ve `configparser` yaygın veri
biçimlerini okur ve yazar. **Tarih ve takvim:** `datetime` tarih ve zaman aritmetiğini,
`calendar` takvim sorgularını, `zoneinfo` saat dilimlerini taşır.

**Dosya sistemi ve kaynak:** `pathlib` yol nesnelerini, `shutil` toplu dosya işlerini,
`tempfile` geçici alanı, `glob` örüntüyle dosya seçmeyi verir. **Veri yapıları:**
`collections` özelleşmiş kapları, `heapq` öncelikli erişimi, `bisect` sıralı listede yer
bulmayı, `array` tek tipli sayı dizisini sağlar.

**Fonksiyonel araçlar:** `itertools` yineleyici birleştirme ve dilimlemeyi, `functools`
sarmalama ve önbelleklemeyi, `operator` işleçlerin fonksiyon karşılıklarını verir.
**Sayı:** `math`, `statistics`, `decimal`, `fractions` ve `random` ayrı sayı alanlarını
kapsar. **Tip ve sözleşme:** `typing`, `dataclasses`, `enum` ve `abc`.

**Sistem ve çalıştırma:** `sys`, `os`, `subprocess`, `argparse` ve `logging`.
**Eşzamanlılık:** `threading`, `multiprocessing`, `asyncio` ve `concurrent.futures`.
**Ağ ve aktarım:** `socket`, `http`, `urllib`. **Sıkıştırma ve özet:** `zipfile`,
`tarfile`, `gzip`, `hashlib`, `hmac`, `secrets`. **Sınama:** `unittest` ve `doctest`.

Bu bir liste olarak ezberlenmez; işe yarar okuma, bir işe başlarken o işin **hangi öbeğe**
düştüğünü sormaktır. Öbek bulunduğunda modül de bulunur.

#### Kitaplığı İçe Aktarmanın Bedeli

Standart kitaplığın modülleri de bu konunun ilk iki dersinde ölçülen kuralın içindedir; ayrı
bir düzenekleri yoktur. Bir kitaplık modülü ilk kez istendiğinde gövdesi bir kez koşar, modül
nesnesi var edilir ve önbelleğe yazılır; aynı modülün sonraki bütün istekleri **0** gövde
koşturur ve yeni nesne var etmez. İlk derste ölçülen `is` karşılaştırması bunu doğrudan
gösteriyordu.

Aynı biçimde, kitaplığın paket olan parçaları için ikinci dersin peşin içe aktarma ölçümü
geçerlidir: bir paketin alt modülü istendiğinde zincirdeki her düzeyin gövdesi koşar. Bu,
kitaplığı kullanmanın bir bedeli olduğu anlamına gelir — ama **bir kez** ödenen bir bedeldir
ve elle yazımın ödettiği bedelle aynı türden değildir. Elle yazım her çağrıda çalışır;
içe aktarma bir kez çalışır, sonra yalnız bir sözlük araması kadar iş yapar.

Bu yüzden "gereksiz içe aktarmadan kaçınmak" ile "gereksiz nesne var etmekten kaçınmak" ayrı
iki iştir. Birincisi bir kereliktir ve modül sayısıyla ölçülür; ikincisi her çağrıda tekrar
eder ve öğe sayısıyla ölçülür. Ölçümün ilk tablosu ikinci türdendir.

#### Üç İş, İki Yazım

Ölçüm üç iş üzerinde yapılıyor ve her iş iki kez yazılıyor. Birincisi **iki kaynaktan gelen
öğelerin ilk üçünü almak**: elle yazımda iki liste kurulup birleştirilir ve baştan üç öğe
dilimlenir; kitaplıkla yazımda `itertools` iki akışı zincirler ve baştan üç öğe alınır.

İkincisi **bir dosya adını kök ve uzantı olarak ayırmak**: elle yazımda ad son noktadan
bölünür; kitaplıkla yazımda `pathlib` bir yol nesnesi kurar ve iki parçayı ondan okur.
Üçüncüsü **bir tarihe gün eklemek**: elle yazımda ay uzunlukları ve artık yıl kuralı
yazılır; kitaplıkla yazımda `datetime` iki nesneyi toplar.

Üç iş bilerek farklı türden seçilmiştir. Birincisinde iki yazım aynı sonucu verir ve ayrım
yalnız maliyettedir; ikincisinde ve üçüncüsünde ise **sonucun kendisi** kimi girdide ayrılır.
Bu, "kitaplık daha kısa yazdırır" gibi tek boyutlu bir okumayı baştan engeller: kazanç kimi
işte nesnede, kimi işte satırda, kimi işte doğrulukta ortaya çıkar.

Satır sayısının dürüst ölçülmesi için altı yazımın hepsi birer kaynak metni olarak tutulur;
satır sayısı o metinden sayılır ve işlevler aynı metinden üretilir. Böylece sayılan satırla
koşan satır aynı olur.

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

- **MP20** — `Oge` sınıfı ortak tanımdan alınmıştır; sayma düzeni değiştirilmemiştir ve
  sayaç her ölçümden önce sıfırlanır.
- **MP21** — Akış ölçümünde iki kaynağın her biri **400** değer taşır; toplam **800**'dür.
- **MP22** — "Ara kapta tutulan", işlev sonucu döndürdüğü anda ara listede duran öğe
  sayısıdır; tembel yazımda böyle bir liste kurulmadığı için **0** yazılır.
- **MP23** — Satır sayısı, işlevin kaynak metninin boş uçları atılmış satır sayısıdır;
  aynı metin koşturulan işlevi üretir.
- **MP24** — Altı ad ve altı tarih **elle seçilmiştir**; kenar durumları kapsayacak
  biçimde, rastgele değil.
- **MP25** — Kâhin standart kitaplığın sonucudur; elle yazım ona göre ayrışır ya da uyar.
- **MP26** — Elle yazılan artık yıl kuralı **dörde bölünebilirlik** kuralıdır; yüzyıl
  düzeltmesi bilerek yazılmamıştır.
- **MP27** — Gün ekleme ölçümündeki tarihler yıl, ay ve yüzyıl sınırlarını kapsar; sonuçlar
  yıl, ay, gün üçlüsü olarak karşılaştırılır.
- **MP28** — Ölçüm hiçbir dosyaya dokunmaz; yol nesneleri yalnız ad olarak kurulur ve
  dosya sistemine sorulmaz.

#### Ölçüm

```python
"""Standart kitaplik: ayni is elle yazildiginda kac nesne ve kac satir."""

from datetime import date, timedelta
from itertools import chain, islice
from pathlib import PurePosixPath

SAYAC = {"uretilen": 0}


class Oge:
    """Her uretimi sayan oge; ortak tanimin sayma duzeni degismez."""

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


KAYNAK = {
    "elle_akis": """
def elle_akis(a, b):
    hepsi = [Oge(d) for d in a] + [Oge(d) for d in b]
    return hepsi[:3], len(hepsi)
""",
    "kitaplikla_akis": """
def kitaplikla_akis(a, b):
    akis = chain((Oge(d) for d in a), (Oge(d) for d in b))
    return list(islice(akis, 3)), 0
""",
    "elle_ad_ayir": """
def elle_ad_ayir(ad):
    taban = ad.rsplit("/", 1)[-1]
    if "." not in taban:
        return taban, ""
    kok, nokta, son = taban.rpartition(".")
    return kok, nokta + son
""",
    "kitaplikla_ad_ayir": """
def kitaplikla_ad_ayir(ad):
    yol = PurePosixPath(ad)
    return yol.stem, yol.suffix
""",
    "elle_gun_ekle": """
def elle_gun_ekle(y, a, g, gun):
    uzunluk = [31, 28, 31, 30, 31, 30, 31, 31, 30, 31, 30, 31]
    for _ in range(gun):
        son = uzunluk[a - 1]
        if a == 2 and y % 4 == 0:
            son = 29
        g += 1
        if g > son:
            g, a = 1, a + 1
            if a > 12:
                a, y = 1, y + 1
    return y, a, g
""",
    "kitaplikla_gun_ekle": """
def kitaplikla_gun_ekle(y, a, g, gun):
    sonuc = date(y, a, g) + timedelta(days=gun)
    return sonuc.year, sonuc.month, sonuc.day
""",
}

AD_ALANI = {"Oge": Oge, "chain": chain, "islice": islice, "date": date,
            "PurePosixPath": PurePosixPath, "timedelta": timedelta}
for kaynak in KAYNAK.values():
    exec(kaynak, AD_ALANI)


def satir(ad):
    return len(KAYNAK[ad].strip().splitlines())


def akisi_olc(islev, n=400):
    SAYAC["uretilen"] = 0
    sonuc, ara = AD_ALANI[islev](range(n), range(n))
    return SAYAC["uretilen"], ara, len(sonuc)


ADLAR = ["veri.csv", "arsiv.tar.gz", ".gizli", "klasor/ad.txt", "ad"]
TARIHLER = [(2024, 2, 28, 1), (2023, 2, 28, 1), (2100, 2, 28, 1),
            (2000, 2, 28, 1), (2024, 12, 31, 1), (2024, 1, 31, 45)]

print(f"{'iki kaynaktan ilk üç öğe':<26s} {'satır':>5s} {'var edilen':>10s}"
      f" {'ara kapta tutulan':>17s} {'döndürülen':>10s}")
for ad, etiket in (("elle_akis", "elle"), ("kitaplikla_akis", "kitaplıkla")):
    uretim, ara, dondurulen = akisi_olc(ad)
    print(f"  {etiket:<24s} {satir(ad):5d} {uretim:10d} {ara:17d}"
          f" {dondurulen:10d}")

print()
print(f"ad ayırma: elle {satir('elle_ad_ayir')} satır, "
      f"kitaplıkla {satir('kitaplikla_ad_ayir')} satır")
ayrisan = [(a, AD_ALANI["elle_ad_ayir"](a), AD_ALANI["kitaplikla_ad_ayir"](a))
           for a in ADLAR
           if AD_ALANI["elle_ad_ayir"](a) != AD_ALANI["kitaplikla_ad_ayir"](a)]
print(f"denenen ad {len(ADLAR)}, ayrışan {len(ayrisan)}")
for a, e, k in ayrisan:
    print(f"  {a:16s} elle {e}  kitaplıkla {k}")

print()
print(f"gün ekleme: elle {satir('elle_gun_ekle')} satır, "
      f"kitaplıkla {satir('kitaplikla_gun_ekle')} satır")
sapan = [(t, AD_ALANI["elle_gun_ekle"](*t), AD_ALANI["kitaplikla_gun_ekle"](*t))
         for t in TARIHLER
         if AD_ALANI["elle_gun_ekle"](*t) != AD_ALANI["kitaplikla_gun_ekle"](*t)]
print(f"denenen tarih {len(TARIHLER)}, sapan {len(sapan)}")
for t, e, k in sapan:
    print(f"  {t}  elle {e}  kitaplıkla {k}")
```

```
iki kaynaktan ilk üç öğe   satır var edilen ara kapta tutulan döndürülen
  elle                         3        800               800          3
  kitaplıkla                   3          3                 0          3

ad ayırma: elle 6 satır, kitaplıkla 3 satır
denenen ad 5, ayrışan 1
  .gizli           elle ('', '.gizli')  kitaplıkla ('.gizli', '')

gün ekleme: elle 12 satır, kitaplıkla 3 satır
denenen tarih 6, sapan 1
  (2100, 2, 28, 1)  elle (2100, 2, 29)  kitaplıkla (2100, 3, 1)
```

#### Aynı Satır, Farklı Nesne

İlk tablo bu dersin en aykırı sonucudur: iki yazımın **satır sayısı eşit**, ikisi de **3**.
Kitaplık burada yazı kısaltmıyor. Ayrıldıkları yer sayılan nesnede: elle yazım **800** öğe
var ediyor, kitaplıkla yazım **3**. Döndürülen ikisinde de **3**.

Fark, işin **hangi noktada** yapıldığından geliyor. Elle yazımda iki liste kavramayla
kuruluyor; kavrama kurulduğu anda bütün öğeleri var ediyor, sonra ikisi birleştiriliyor ve
ancak en sonda ilk üçü alınıyor. Öbür yazımda `itertools` iki akışı **tüketmeden**
zincirliyor ve baştan üç öğe istendiğinde yalnız üç öğe var ediliyor; geri kalan 797 hiç
üretilmiyor.

Ara kap sütunu tutmayı ayrı gösteriyor: elle yazımda sonuç döndüğü anda **800** öğe hâlâ bir
listede duruyor, kitaplıkla yazımda **0**. Bu, kursun ikinci iddiasının standart kitaplık
üzerinden görünen yüzüdür — burada tembel olan yazım, üretimi geciktirmekle kalmıyor, **hiç
istenmeyeni hiç üretmiyor**, çünkü akış sonuna kadar tüketilmiyor.

Bu ayrımın sınırını yanlış okumamak gerekir. Kitaplığın zincirlediği akış **üretimden
kaçınmıyor**; yalnız üretimi tüketime bağlıyor. Aynı akış sonuna kadar tüketilseydi o da
**800** öğe var ederdi ve iki yazım arasındaki üretim farkı sıfırlanırdı; ayakta kalan tek
fark, ara listede tutulan öğe sayısı olurdu. Kazancın kaynağı tembelliğin kendisi değil,
**istenenin azlığıdır**: üç öğe istendi, üç öğe üretildi.

Buradan çıkan kural şudur: **standart kitaplığın kazandırdığı şey her zaman satır değildir.**
Kimi modül yazıyı kısaltır, kimi de yazıyı aynı bırakıp var edilen nesneyi düşürür. İkisi
ayrı kazançtır ve ayrı ölçülür. Bir modülü seçerken sorulacak soru "kaç satır kısaltır"
değil, "hangi ekseni değiştirir" olmalıdır.

#### Satır Farkı ve Sessiz Ayrışma

İkinci ve üçüncü ölçümde tablo tersine dönüyor: satır farkı büyük. Ad ayırma elle **6**,
kitaplıkla **3** satır; gün ekleme elle **12**, kitaplıkla **3**. Dörtte bir oranında bir
fark, ama asıl bedel satırlarda değil.

Ad ayırmada denenen **5** adın **1**'i ayrışıyor. Baştan noktalı `.gizli` adında elle yazım
kökü boş bırakıp uzantıyı `.gizli` sayıyor; kitaplık ise adın tamamını kök, uzantıyı boş
kabul ediyor. Çok uzantılı `arsiv.tar.gz` ve dizinli `klasor/ad.txt` adlarında iki yazım
uyuşuyor — ayrışma **her** durumda değil, yalnız kenarlarda.

Gün eklemede denenen **6** tarihin **1**'i sapıyor ve sapma bir yüzyıl sınırında. Elle
yazılan artık yıl kuralı dörde bölünebilirliğe bakıyor; 2100 dörde bölündüğü için elle yazım
şubatı 29 gün sayıyor ve **(2100, 2, 29)** veriyor. Kitaplık **(2100, 3, 1)** veriyor, çünkü
yüzyıl kuralını da uyguluyor. Yıl sınırı, artık yıl ve kırk beş günlük atlama gibi geri kalan
beş durumda iki yazım aynı sonucu veriyor.

İki ölçümün ortak dersi budur: **elle yazım yanlış sonucu gürültüyle vermez.** Hiçbir istisna
fırlamıyor, hiçbir uyarı çıkmıyor; sonuç yalnızca sessizce başka. Denenen durumların
çoğunda uyuşma olduğu için sınama kümesi kenarları içermiyorsa ayrışma hiç görünmez. Standart
kitaplığın esas kazandırdığı şey, o kenar durumların **zaten yazılmış** olmasıdır.

#### İki Ayrışmanın Aynı Türden Olmadığı

Sayılar aynı sütunda dursa da iki ayrışma aynı şey değildir ve karıştırılmaları bu ölçümün
en kolay yanlış okumasıdır.

Gün eklemedeki sapma bir **yanlıştır**. Takvim kuralı dilin bir tercihi değil, dışarıda
tanımlı bir kuraldır; yüzyıl düzeltmesini yazmayan yazım o kurala uymamıştır ve verdiği
tarih hiçbir okumada doğru değildir. Burada kitaplık kâhindir, çünkü kuralın tamamını
uygular.

Ad ayırmadaki ayrışma ise bir **sözleşme farkıdır**. Baştan noktalı bir adın uzantısı olup
olmadığı takvim gibi dışarıdan belirlenmiş bir olgu değildir; kitaplık bir kural seçmiştir,
elle yazım başka bir kural seçmiştir. Gizli dosya adlarının uzantısız sayılması yaygın bir
sözleşmedir ve kitaplık onu uygular; ama bir uygulama bunun tersine ihtiyaç duyabilir.
Ayrışan **1** durum, elle yazımın kusuru değil, iki sözleşmenin ayrıldığı noktadır.

Buradan çıkan ölçüt şudur: bir işi elle yazma kararı, kitaplığın sözleşmesinin işe
uymadığını **gösterebildiğinde** doğrudur. Kitaplığın ne yaptığı bilinmeden yazılan elle
çözüm bu ölçütü karşılamaz; farkı bilerek yazılan çözüm karşılar. İki durumun kaynak metni
aynı görünür, ayrımı yapan tek şey farkın ölçülmüş olmasıdır.

#### Özet

- Standart kitaplık dilin parçasıdır; metin, veri biçimi, takvim, dosya sistemi, veri
  yapısı, fonksiyonel araç, sayı, sistem, eşzamanlılık, ağ ve sınama öbeklerini kapsar.
- Akış işinde iki yazımın satır sayısı eşittir (**3** ve **3**) ama elle yazım **800** öğe
  var eder, tembel yazım **3**; ara kapta tutulan **800**'e karşı **0**'dır.
- Kitaplığın kazancı her zaman satır değildir: kimi modül yazıyı kısaltır, kimi de var
  edilen nesneyi düşürür; iki kazanç ayrı ölçülür.
- Ad ayırmada elle yazım **6** satır tutar ve denenen **5** addan **1**'inde kitaplıktan
  ayrışır; ayrışma yalnız kenar adlardadır.
- Gün eklemede elle yazım **12** satır tutar ve denenen **6** tarihten **1**'inde sapar;
  sapma yüzyıl kuralının yazılmamasından gelir.
- Elle yazımın yanlışı istisna fırlatmaz; sonuç sessizce başka çıkar, bu yüzden kenar
  durumları içermeyen bir sınama kümesi ayrışmayı hiç göstermez.

#### Sonraki Adım

Bu derste her şey içe aktarıldı: kitaplık modülleri de, ölçüm işlevleri de bir gövdenin
içinde tanımlandı ve dışarıdan çağrıldı. Oysa aynı dosya iki ayrı biçimde çalışabilir — biri
içe aktarılan modül olarak, öbürü doğrudan koşturulan betik olarak. İki biçimde de aynı gövde
koşar ama modülün kendi adı değişir ve o ada bakan bir blok yalnız bir durumda çalışır.
Sonraki ders bu ikiliği ölçüyor: aynı dosya iki biçimde koşturulduğunda **kaç modül nesnesi**
var edilir ve bir modül yeniden yüklendiğinde eski nesnelere ne olur?

### Yeniden Yüklenebilirlik ve Ana Blok

Source: https://academia.sh/tr/kurslar/python-veri-yapilari/yeniden-yuklenebilirlik-ve-ana-blok

Aynı dosya içe aktarıldığında bir, betik olarak koşturulduğunda ikinci bir modül nesnesi var eder ve ana blok yalnız ikincisinde çalışır; yeniden yükleme modül nesnesini korur ama içindeki her işlevi yeniler.

Önceki derste her şey içe aktarıldı: kitaplık modülleri de, ölçüm işlevleri de bir gövdenin
içinde tanımlandı ve dışarıdan çağrıldı. Oysa aynı dosya iki ayrı biçimde çalışabilir. Biri
buraya kadar ölçülen biçimdir — dosya bir **modül** olarak istenir, gövdesi koşar ve adları
başkalarının kullanımına açılır. Öbürü doğrudan koşturmadır: dosya bir **betik** olarak
başlatılır ve programın giriş noktası olur.

İki biçimde de aynı gövde koşar. Ayrılan tek şey, modülün **kendi adına** ne yazıldığıdır ve
o ada bakan bir blok yalnız bir durumda çalışır. Bu dersin sorusu şudur: aynı dosya iki
biçimde koşturulduğunda **kaç modül nesnesi** var edilir, hangi kayıt hangisini tutar ve bir
modül yeniden yüklendiğinde ondan alınmış eski nesnelere ne olur?

#### Modülün Kendi Adı

Her modül nesnesinin sözlüğünde `__name__` adında bir giriş vardır ve içe aktarma bu girişi
modülün **önbellekteki adıyla** doldurur. Bir dosya içe aktarıldığında oraya dosyanın modül
adı yazılır. Aynı dosya doğrudan koşturulduğunda ise oraya `"__main__"` yazılır, çünkü o
koşumun bir içe aktarma adı yoktur — istenmemiş, başlatılmıştır.

**Ana blok (main block)** bu ayrımı okuyan koşuldur. Gövdenin sonunda `__name__` değerini
`"__main__"` ile karşılaştıran bir blok, dosya betik olarak koşturulduğunda çalışır, içe
aktarıldığında çalışmaz. Bir dosyanın hem kitaplık hem giriş noktası olabilmesinin yolu
budur: tanımlar gövdede durur ve herkese açıktır, çalıştırma ise bloğun içinde durur ve
yalnız doğrudan koşumda işler.

Bunun ölçülebilir bir sonucu vardır ve ilk derste kurulan kuralla çelişmez, ondan çıkar:
önbellek kaydı **ada göre** tutulur. Betik olarak koşan dosya `__main__` adıyla kaydedilir,
içe aktarılan aynı dosya kendi adıyla. İki ayrı ad, iki ayrı kayıt, **iki ayrı modül
nesnesi** demektir — aynı dosyadan.

#### Yeniden Yükleme Neyi Değiştirir

Modül önbelleği bir kaydı sildiğinde sonraki istek yeni bir nesne var eder; bu ilk derste
ölçüldü. **Yeniden yükleme** ise başka bir iştir: gövdeyi yeniden koşturur ama **var olan
modül nesnesinin sözlüğüne** yazar. Modül nesnesi aynı kalır, içindeki adlar tazelenir.

Ayrımın iki gözlenebilir sonucu vardır ve ölçüm ikisini de sınıyor: gövde yeniden koştuğu
için her tanım **yeni bir nesne** üretir ve yazma işlemi sözlüğü **boşaltmadan** yapılır.

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

- **MP29** — Ölçüm depoya dosya bırakmaz; kaynaklar geçici bir dizinde üretilir ve
  **dizinin yolu basılmaz.**
- **MP30** — Ölçülen dosya her koşumunda kendi adını ortak bir listeye yazar; sayan taraf
  ölçülen dosyanın kendisidir.
- **MP31** — Betik koşumu ayrı bir süreç açmaz. Aynı yorumlayıcıda, modülün kendi adı
  `__main__` olacak biçimde koşturulur; ölçüm bu yüzden ortak listeyi okuyabilir.
- **MP32** — Ana blok yalnız listeye bir kayıt düşer; içinde başka iş yapılmaz.
- **MP33** — Yeniden yüklemeden önce kaynak metninde **tek bir dizge** değiştirilir; değişen
  tek şey işlevin döndürdüğü değerdir.
- **MP34** — Yeniden yüklemeden önce modüle dışarıdan bir ad eklenir ve o adın akıbeti ayrıca
  okunur.
- **MP35** — Eski işlev nesnesi yeniden yüklemeden önce ayrı bir ada bağlanır;
  karşılaştırmalar `is` iledir ve **kimlik sayısı basılmaz.**
- **MP36** — Ölçüm bittiğinde eklenen yol girdisi ve bütün önbellek kayıtları geri alınır.

#### Ölçüm

```python
"""Ana blok ve yeniden yukleme: ayni dosya kac modul nesnesi var eder."""

import importlib
import runpy
import sys
import tempfile
from pathlib import Path

ARAC = """import kayit

kayit.OLAY.append(('govde', __name__))


def selam():
    return 'birinci'


if __name__ == '__main__':
    kayit.OLAY.append(('ana blok', __name__))
"""
KAYIT = "OLAY = []\n"


def kur(kok):
    (kok / "kayit.py").write_text(KAYIT, encoding="utf-8")
    (kok / "arac.py").write_text(ARAC, encoding="utf-8")
    sys.path.insert(0, str(kok))
    importlib.invalidate_caches()


def sok(kok):
    sys.path.remove(str(kok))
    for ad in ("kayit", "arac"):
        sys.modules.pop(ad, None)


def olc(kok):
    kur(kok)
    kayit = importlib.import_module("kayit")
    arac = importlib.import_module("arac")
    ice_aktarma = list(kayit.OLAY)

    kayit.OLAY.clear()
    betik = runpy.run_module("arac", run_name="__main__")
    betik_olay = list(kayit.OLAY)
    ayri_nesne = betik["selam"] is not arac.selam
    onbellek_bozuldu = sys.modules["arac"] is arac

    kayit.OLAY.clear()
    arac.EKLENEN = "yeniden yuklemeden once"
    eski_selam = arac.selam
    (kok / "arac.py").write_text(ARAC.replace("'birinci'", "'ikinci'"),
                                 encoding="utf-8")
    importlib.invalidate_caches()
    yeni = importlib.reload(arac)
    sonuc = {
        "ice_aktarma": ice_aktarma,
        "betik": betik_olay,
        "ayri_nesne": ayri_nesne,
        "onbellek_bozuldu": onbellek_bozuldu,
        "yeniden_olay": list(kayit.OLAY),
        "ayni_modul": yeni is arac,
        "eski_islev": eski_selam is arac.selam,
        "eski_deger": eski_selam(),
        "yeni_deger": arac.selam(),
        "eklenen_kaldi": getattr(arac, "EKLENEN", "silindi"),
    }
    sok(kok)
    return sonuc


with tempfile.TemporaryDirectory() as d:
    s = olc(Path(d))

print(f"içe aktarmada koşan: {s['ice_aktarma']}")
print(f"betik olarak koşumda koşan: {s['betik']}")
print(f"betik ad alanındaki işlev modüldekinden ayrı nesne -> {s['ayri_nesne']}")
print(f"önbellekteki kayıt bozulmadı -> {s['onbellek_bozuldu']}")
print()
print(f"yeniden yüklemede koşan: {s['yeniden_olay']}")
print(f"modül nesnesi aynı -> {s['ayni_modul']}, "
      f"işlev nesnesi aynı -> {s['eski_islev']}")
print(f"eski ada bağlı işlev {s['eski_deger']}, "
      f"modüldeki işlev {s['yeni_deger']} veriyor")
print(f"yeniden yüklemeden önce eklenen ad -> {s['eklenen_kaldi']}")
```

```
içe aktarmada koşan: [('govde', 'arac')]
betik olarak koşumda koşan: [('govde', '__main__'), ('ana blok', '__main__')]
betik ad alanındaki işlev modüldekinden ayrı nesne -> True
önbellekteki kayıt bozulmadı -> True

yeniden yüklemede koşan: [('govde', 'arac')]
modül nesnesi aynı -> True, işlev nesnesi aynı -> False
eski ada bağlı işlev birinci, modüldeki işlev ikinci veriyor
yeniden yüklemeden önce eklenen ad -> yeniden yuklemeden once
```

#### İki Koşum, İki Nesne

İlk iki satır ana bloğun kuralını doğrudan veriyor. İçe aktarmada koşan tek şey gövdedir ve
modülün kendi adı `arac`'tır; ana blok kaydı **yok**. Betik olarak koşumda aynı gövde yeniden
koşuyor, ama bu kez modülün kendi adı `__main__` ve ana blok da çalışıyor. Aynı dosya, aynı
satırlar, iki farklı sonuç — ayrımı yapan tek şey ada yazılan değer.

Üçüncü satır bedeli gösteriyor: betik koşumunun ad alanındaki işlev, modüldeki işlevden
**ayrı bir nesne**. Gövde ikinci kez koştuğu için `def` deyimi ikinci bir işlev nesnesi var
etti. Aynı dosyadan var edilen modül nesnesi **2**, aralarında paylaşılan tanım **0**.
Dördüncü satır bunu tamamlıyor: önbellekteki `arac` kaydı bozulmadı, hâlâ ilk nesneye bağlı.
İki koşum yan yana duruyor ve birbirini görmüyor.

Bu, ana bloğa iş yazmanın neden sınırlı olduğunu açıklar. Bir dosya hem içe aktarılıp hem
betik olarak koşturulursa, iki koşum aynı adları taşıyan **iki ayrı ad alanı** üretir. Modül
düzeyinde tutulan bir sayaç ya da önbellek iki kez var edilir; birine yazılan öbüründe
görünmez. Ölçümde bunun görünmemesinin tek nedeni, sayılan listenin `arac`'ta değil, ikisinin
de paylaştığı `kayit` modülünde durmasıdır. Kural şudur: **giriş noktası bir kabuktur** —
durum ve tanım gövdede kalır, ana blok yalnız bir işlevi çağırır.

#### Yeniden Yükleme

Alt bölüm ayrı bir düzeneği ölçüyor. Yeniden yüklemede koşan yine gövdedir ve modülün kendi
adı **`arac`** olarak kalıyor — betik koşumundan farkı bu. Sonraki satır ayrımın kalbini
veriyor: modül nesnesi **aynı**, işlev nesnesi **değil**. Gövde yeniden koştuğu için `def`
yeni bir işlev nesnesi var etti ve modülün sözlüğündeki ad ona bağlandı; modül nesnesinin
kendisi hiç değişmedi.

Üçüncü satır bunun kimi kırdığını gösteriyor. Yeniden yüklemeden önce işlevi kendi adına
almış olan taraf **eski** nesneyi tutuyor ve o nesne **birinci** veriyor; modül üzerinden
çağrılan işlev **ikinci** veriyor. Değişikliği yalnız modülün sözlüğünden okuyanlar gördü.
Var edilen işlev nesnesi **2**, tutulan **2**, paylaşılan **0**. İlk dersin ölçtüğü ayrım
burada yeniden ödeniyor: içe aktarma nesneyi paylaşır, adı kopyalar — kopyalanmış ad
tazelenmez.

Son satır sözlüğün boşaltılmadığını doğruluyor: yeniden yüklemeden önce modüle dışarıdan
eklenen ad **yerinde duruyor**. Yeniden yükleme bir temizlik değil, bir üzerine yazmadır.
Yeni gövdede karşılığı olmayan her ad — silinmiş bir tanım, dışarıdan eklenmiş bir öznitelik
— eski değeriyle kalır.

İki ölçümün birlikte söylediği şudur: **bir kaynağı yeniden koşturmanın üç ayrı sonucu
vardır.** Önbellek kaydı silinip yeniden istenirse yeni bir modül nesnesi var edilir; yeniden
yükleme yapılırsa modül nesnesi korunur, içindekiler yenilenir; betik olarak koşturulursa
ikinci bir ad altında ikinci bir nesne var edilir ve ilki olduğu gibi kalır. Üçünde de gövde
bir kez daha koşar, ama var edilen ve tutulan nesneler üçünde de başkadır.

#### Özet

- Modülün kendi adı içe aktarmada modül adıyla, doğrudan koşumda `__main__` ile doldurulur;
  ana blok yalnız ikincisinde çalışır.
- Aynı dosya iki biçimde koşturulduğunda **2** modül nesnesi var edilir; iki ad alanının
  paylaştığı tanım **0**'dır ve içe aktarmadan gelen önbellek kaydı bozulmaz.
- Ana blok bir kabuk olarak yazılır: tanım ve durum gövdede kalır, çünkü gövde iki koşumda da
  çalışır ve modül düzeyindeki her durum iki kez var edilir.
- Yeniden yükleme modül nesnesini **korur**, gövdeyi yeniden koşturur ve içindeki adları
  yeni nesnelere bağlar; önceden alınmış eski işlev nesnesi eski değeri vermeye devam eder.
- Yeniden yükleme sözlüğü boşaltmaz; yeni gövdede karşılığı olmayan adlar eski değerleriyle
  yerinde kalır.

#### Kurs Kapanışı

Kurs tek bir soruyla yürüdü: yazılan biçim **kaç nesne var etti, kaçını tuttu, neyi
paylaştı?** Sonucun kendisi hiçbir derste ölçü sayılmadı, çünkü aynı sonucu veren iki
yazımın sayıları her seferinde ayrıldı.

| Ders | Ölçülen biçim | Var edilen / tutulan / paylaşılan |
|---|---|---|
| `koleksiyonlar/01` Listeler | bin öğelik liste ve sekiz büyütme biçimi | 1000 var edilir ve tutulur; ikinci ada bağlamak 0 var eder, `[tek] * 5` 0 var edip beş yuvayı paylaştırır |
| `koleksiyonlar/02` Demetler | değişmezliğin kapattığı altı işlem ve `k += (yeni,)` | 1 var edilir; kap yenidir, öğeler paylaşılır (`buyuk[0] is demet[0]` → True); çoklu dönüş 0 var eder |
| `koleksiyonlar/03` Sözlükler | iki yüz öğede üyelik ve `keys()` görünümü | listede 200, kümede ve sözlükte 1 karşılaştırma; görünüm sözlüğü paylaşır ve 2'den 3'e çıkar, anlık kopya 1000 yuva tutar |
| `koleksiyonlar/04` Kümeler | üç yüz öğenin tekilleştirilmesi ve altı küme işlemi | elle 15050, kümeyle 200 karşılaştırma; altı işlem 0 öğe var eder ve sonuçtaki her nesne paylaşılır |
| `koleksiyonlar/05` Özelleşmiş Koleksiyonlar | aynı gruplamanın üç yazımı ve bin öğelik akışta kırpma | `setdefault` 320, varsayılan sözlük 100 kap var eder; akışta üç yazım da 1000 üretir, tutulan 1000'e karşı 5; döndürme listede 3 ara kap, kuyrukta 0 |
| `koleksiyonlar/06` Dilimleme | sekiz dilim biçimi ve iç içe listenin kopyası | sekizi de 0 öğe var edip yeni kap döndürür; iç içe listede 4 üretilir ve dilim kopyasında iki iç listenin ikisi de paylaşılır, derin kopya 4 verir |
| `yineleme/01` Yineleyici Protokolü | `iter` ve `next` sözleşmesi, üç geçiş biçimi | `iter` 0 başvuru yazar, `list()` 3 yazar, kavrama 3 yeni nesne var eder; ilk ikisi öğeleri paylaşır, sonuncusu 0 |
| `yineleme/02` Üreteçler | `yield` içeren işlevin çağrısı ve `yield from` | kurulumda kavrama 1000, üreteç 0 var eder; üç öğede 3'te kalır, `yield from` ilk öğeye dek 1 |
| `yineleme/03` Üreteç İfadeleri | bin öğelik toplamanın iki yazımı | ikisi de 1000 var eder ve 499500 verir; listede tutulan 0'a karşı 1000 |
| `yineleme/04` Kavrama Sözdizimi | liste, küme, sözlük kavraması ve süzme koşulunun yeri | 1000 / 100 / 100 tutulur, paylaşılan 1000 / 0 / 100; süzme üretimden önce 5, sonra 10 var eder |
| `yineleme/05` Lambda ve Yüksek Mertebeden Fonksiyonlar | anahtar işlevinin kaç kez çağrıldığı ve `map` ile `filter` | anahtar öğe başına 1 kez, toplam 200 çağrı; elle yazılan sıralamada 39800; `sorted` yeni kap kurar ve 200 öğeyi paylaşır, `map` kurulumda 0 var eder |
| `yineleme/06` Dekoratörler | sarmalamanın kurduğu katman ve meta veri koruma | sarmasız 1, tek dekoratör 2, iki dekoratör 3 katman; ada bağlı nesne değişir, özgün işlev paylaşılır (`__wrapped__ is hesapla` → True); not alan sarmalayıcı 20 isteği 5 çağrıya indirip 5 öğe tutar |
| `moduller-ve-paketler/01` Modül Sistemi | üç içe aktarma isteği ve arama yolu sırası | 2 modül nesnesi var edilir, 1 tutulur; iki ad aynı nesneyi paylaşır ve ortak kayıt modülü 1 kalır |
| `moduller-ve-paketler/02` Paket Yapısı | göreli ve mutlak yazımın çözdüğü ad | tek istek 5 gövde koşturur ve 5 nesne tutar; göreli ile mutlak 0 paylaşır (`is` → False) |
| `moduller-ve-paketler/03` Standart Kütüphane Turu | aynı üç işin kitaplıkla ve elle yazımı | elle 800 var eder ve 800 tutar, kitaplıkla 3 var eder ve 0 tutar; sonuç ikisinde de 3 |
| `moduller-ve-paketler/04` Yeniden Yüklenebilirlik ve Ana Blok | içe aktarma, betik koşumu ve yeniden yükleme | betik koşumu 2. modül nesnesini var eder, ilki tutulur, paylaşılan tanım 0; yeniden yükleme modülü paylaşır, işlevi yeniler |

Tabloda dört okuma birbirini kesiyor. **Tembellik üretimi azaltmaz, tutmayı azaltır:**
bin öğelik toplamada iki yazım da 1000 nesne üretir ve ikisi de 499500 verir; ayrıldıkları
tek yer listede tutulandır. **Kopya bir katman derinliğindedir:** yeni kap kuran her işlem
dış kabı yeniler, içindekileri paylaşır — demette de, dilimlemede de, küme işlemlerinde de.
**Maliyeti veri değil erişim biçimi belirler:** aynı iki yüz öğe listede 200, kümede 1
karşılaştırma ister. **Modül de bir nesnedir ve bir kez var edilir:** son konu aynı sayma
düzenini dosya sınırının ötesine taşıdı ve içe aktarmanın da bir var etme işi olduğunu
ölçtü.

Kurs boyunca kaplar hazır durdu. Liste, demet, sözlük, küme, üreteç ve modül — hepsi dilin
verdiği nesnelerdi ve ölçülen şey onların **seçilmesinin** bedeliydi. Bir sonraki kurs bu
sınırın öbür tarafındadır. **Kabı seçmekle kabı yazmak ayrı şeylerdir:** bir nesnenin
uzunluğunun olması, karşılaştırılabilmesi ve bir kapta anahtar olabilmesi seçilen bir
özellik değil, **yazılan** bir sözleşmedir. **Nesneye Dayalı Python ve Tipler** kursu o
sözleşmeyi yazan tarafı kurar: dilin sözdizimine uyan sınıflar, kalıtım yerine bileşimin
seçildiği durumlar ve tip ipuçlarıyla belirginleştirilen sözleşmeler. Burada hangi kabın
kaç nesneye mal olduğu sayıldı; orada o kabın yerine geçecek nesnenin kendisi yazılacak.

