---
title: "Python'un Yürütme Modeli"
source: 'https://academia.sh/tr/kurslar/python-temelleri/pythonun-yurutme-modeli'
course: 'Python Temelleri'
language: tr
updated: '2026-08-17T18:10:28+00:00'
license: 'CC BY-SA 4.0'
---

# Python'un Yürütme Modeli

Kaynak önce bütün olarak derlenir sonra yürütülür; sözdizim ise bir kısaltmadır — on bir biçim 12 ayrı özel yöntemi toplam 18 kez çağırır ve üç öğelik nesnede döngü öğe sayısından bir fazla, 4 kez `__next__` çağırır.

Yazılım Geliştirme Pratiği müfredatı beş kursla bir ekip disiplini kurdu ve kapanışında
ölçtüğü her şeyin **dilden bağımsız** olduğunu söyledi: sayılan şey hiçbir zaman yazılan
kodun kendisi değil, onun çevresindeki işti. Bu müfredat çevreden merkeze döner. Sıra dilin
kendisinde.

Programlama Temelleri kursu örneklerini Python'la yazmıştı; atamanın bir bağlama olduğu,
`is` ile `==` ayrımı, temel veri tipleri, işleçler ve tip dönüşümü orada kuruldu. Ama bir
dili **örnek olarak kullanmak** ile **dilin kendisini** öğrenmek ayrı şeylerdir: orada
Python bir anlatım aracıydı, burada ölçülen nesnedir. Bu kurs sözdizimin **altına** bakar ve
tek bir soruyu on altı derste taşır — yazılan biçim, çalışırken hangi düzeneği çağırıyor ve
kaç kez?

## İki Aşama: Derleme ve Yürütme

Python bir **yorumlayıcı (interpreter)** ile çalışır, ama kaynağı satır satır okuyup hemen
çalıştırmaz. Bir dosya çalıştırıldığında metin önce **sözcük birimlerine (token)** ayrılır,
sonra bir soyut sözdizim ağacına dönüştürülür, sonra **bayt koduna (bytecode)** çevrilir.
Yorumlayıcının yürüttüğü şey bayt koddur, kaynak metin değil.

İki aşama arasındaki sınır gözlenebilir. Derleme, dosyanın **tamamı** üzerinde yapılır:
kaynağın son satırındaki bir sözdizim kusuru, ilk satırdaki deyimin yürütülmesini de
engeller. Yürütme aşamasında doğan bir hata ise ancak kendi sırası geldiğinde ortaya çıkar
ve ondan öncekiler çalışmış olur. Aradaki fark iki yerleşik işlevle ölçülebilir: `compile`
kaynağı derler ve bir kod nesnesi döndürür, `exec` o nesneyi yürütür.

```python
"""Iki asama: kaynak once bir butun olarak derlenir, sonra yurutulur."""

KAYNAK = {
    "eksik parantez": 'yaz = print\nyaz("bir")\nyaz(\n',
    "tanimsiz ad": 'yaz = print\nyaz("bir")\nyazz("iki")\n',
}

for ad, kaynak in KAYNAK.items():
    print(f"--- {ad}")
    try:
        kod = compile(kaynak, "<ders>", "exec")
    except SyntaxError as e:
        print(f"derleme: {type(e).__name__} — hiçbir deyim yürütülmedi")
        continue
    print(f"derleme: sorunsuz, elde edilen {type(kod).__name__} nesnesi")
    try:
        exec(kod, {})
    except Exception as e:
        print(f"yürütme: {type(e).__name__} — kendinden önceki deyimler yürütüldü")
```

```
--- eksik parantez
derleme: SyntaxError — hiçbir deyim yürütülmedi
--- tanimsiz ad
derleme: sorunsuz, elde edilen code nesnesi
bir
yürütme: NameError — kendinden önceki deyimler yürütüldü
```

İkinci kaynakta `bir` satırı basılmış, ondan sonraki çağrı `NameError` ile düşmüştür.
Birinci kaynakta hiçbir şey basılmamıştır — oysa oradaki ilk iki satır kusursuzdur. Ayrım
şudur: **sözdizim kusuru derleme aşamasında, ad kusuru yürütme aşamasında bulunur.** Bir
adın var olup olmadığı derlenirken bilinmez, çünkü ad ancak yürütme sırasında bir nesneye
bağlanır.

Aynı iki adım **etkileşimli kabukta (interactive shell)** da işler; orada derlenen birim
dosyanın tamamı değil, girilen bloktur. Bloğun sözdizimi bozuksa blok hiç yürütülmez.

Derlemenin ürünü olan bayt kodun düzeni dilin tanımının parçası değildir; yorumlayıcının iç
işidir ve okunması bu kursun konusu değil. Okunacak olan şey bayt kodun **çağırdığı**
yöntemlerdir.

## Sözdizim Bir Kısaltmadır

Kursun ölçü ekseni tek bir gözlemden çıkar: Python'da sözdizimin çoğu biçimi bir
**kısaltmadır**. `n + m` yazıldığında yürütülen şey bir toplama değil, bir **çağrıdır** —
`n` nesnesinin `__add__` yöntemi çağrılır ve dönen değer ifadenin değeridir. `for x in n`
bir döngü sözcüğü değil, iki yöntemin ardışık çağrısıdır: önce `__iter__`, sonra tükenene
kadar `__next__`.

Bu yöntemlerin adları dilin tanımında yazılıdır; her birine **özel yöntem (special method)**,
bir biçimin gerektirdiği yöntem kümesine de **protokol** denir. Adları iki alt çizgi
arasında yazılır ve bu, onları sıradan yöntemlerden ayırmak içindir: özel yöntemler
doğrudan çağrılmak için değil, **sözdizim tarafından çağrılmak için** vardır.

Her sözdizim biçimi bir kısaltma değildir ve bu ayrım baştan konmalıdır. Bir ada değer
bağlamak, bir işlev tanımlamak ya da bir değer döndürmek nesneye hiçbir soru sormaz; bu
biçimler adlarla ve akışla ilgilenir, nesnenin içine bakmaz. Kısaltma olanlar, bir nesneyi
**tüketen** biçimlerdir: onu toplayan, içinde dolaşan, bir koşulda sınayan, dilimleyen,
metne çeviren. Ölçülen on bir biçimin tamamı bu ikinci kümedendir.

Bunun doğrudan bir sonucu var: **sözdizim tipin adına bakmaz, yöntemin varlığına bakar.**
`+` işlecinin sayılarda toplama, dizgilerde birleştirme yapması bir ayrıcalık değil, iki
ayrı tipin aynı adlı yöntemi ayrı biçimde tanımlamasıdır. Programlama Temelleri kursunda bu
gözlem **işleç aşırı yükleme** başlığıyla bir kavram olarak kuruldu; burada ölçülen şey
kavram değil, onu gerçekleştiren **addır** — hangi biçim hangi yöntemi kaç kez çağırıyor.

## Ortak Kurgu: Katılımını Kaydeden Nesne

Ölçüm için tek bir sınıf yeter. `Izci`, çok sayıda özel yöntemi tanımlar ve her yöntem
çağrıldığında adını ortak bir listeye yazar. Üzerinde sıradan sözdizim çalıştırılır — `for`,
`in`, `if`, `+`, `+=`, dilim, `str()`, `with`, `==` — ve her biçimin hangi yöntemi kaç kez
çağırdığı listeden okunur.

İkinci bir sınıf, `Bosluksuz`, `Izci`'den türer ve yalnız tek bir şey ekler: `__bool__`.
Aynı `if` ifadesi bu nesnede artık başka bir yöntemi çağıracaktır. Kâhin kurgunun
kendisidir: hangi yöntemin çağrıldığını nesne kendi kaydettiği için biliriz, dolayısıyla
sayı bir çıkarım değil bir dökümdür.

```python
"""Sozdizimin cagirdigi protokol: her ozel yontem cagrildiginda kaydedilir."""

KAYIT = []


def kaydet(ad):
    KAYIT.append(ad)


class Izci:
    """Cok sayida protokole katilan bir nesne; her katilimi kaydeder."""

    def __init__(self, ogeler=(1, 2, 3)):
        self.ogeler = list(ogeler)

    def __iter__(self):
        kaydet("__iter__")
        self._i = 0
        return self

    def __next__(self):
        kaydet("__next__")
        if self._i >= len(self.ogeler):
            raise StopIteration
        deger = self.ogeler[self._i]
        self._i += 1
        return deger

    def __len__(self):
        kaydet("__len__")
        return len(self.ogeler)

    def __contains__(self, x):
        kaydet("__contains__")
        return x in self.ogeler

    def __add__(self, o):
        kaydet("__add__")
        return Izci(self.ogeler + list(o.ogeler))

    def __iadd__(self, o):
        kaydet("__iadd__")
        self.ogeler.extend(o.ogeler)
        return self

    def __getitem__(self, k):
        kaydet("__getitem__")
        return self.ogeler[k]

    def __eq__(self, o):
        kaydet("__eq__")
        return isinstance(o, Izci) and self.ogeler == o.ogeler

    def __str__(self):
        kaydet("__str__")
        return f"Izci{tuple(self.ogeler)}"

    def __repr__(self):
        kaydet("__repr__")
        return self.__str__()

    def __enter__(self):
        kaydet("__enter__")
        return self

    def __exit__(self, tur, deger, iz):
        kaydet("__exit__")
        return False          # istisnayi yutmaz


class Bosluksuz(Izci):
    """__bool__ tanimli: dogruluk sinamasi artik __len__'e dusmez."""

    def __bool__(self):
        kaydet("__bool__")
        return len(self.ogeler) > 0


def olc(islev):
    """Bir sozdizim bicimini calistirir ve cagrilan protokolleri dondurur."""
    KAYIT.clear()
    try:
        islev()
    except Exception as e:
        KAYIT.append(f"!{type(e).__name__}")
    return list(KAYIT)


def dongu():
    for _ in Izci():
        pass


def uyelik():
    9 in Izci()


def dogruluk_len():
    if Izci():
        pass


def dogruluk_bool():
    if Bosluksuz():
        pass


def toplama():
    Izci() + Izci()


def yerinde_toplama():
    a = Izci()
    a += Izci()


def dilim():
    Izci()[0:2]


def metne_cevirme():
    str(Izci())


def baglam():
    with Izci():
        pass


def baglam_istisnali():
    with Izci():
        raise ValueError("ornek")


def esitlik():
    Izci() == Izci()


BICIMLER = (
    ("for x in n", dongu),
    ("x in n", uyelik),
    ("if n  (__len__)", dogruluk_len),
    ("if n  (__bool__)", dogruluk_bool),
    ("n + m", toplama),
    ("n += m", yerinde_toplama),
    ("n[0:2]", dilim),
    ("str(n)", metne_cevirme),
    ("with n", baglam),
    ("with n + istisna", baglam_istisnali),
    ("n == m", esitlik),
)
```

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

- **DT1** — Ölçülen biçimler on bir tanedir; dilin bütün sözdizimi değil, bu kursun ders
  ders ödeyeceği çekirdektir. Kalan biçimler kendi derslerinde ölçülür.
- **DT2** — Kâhin nesnenin kendi kaydıdır: bir yöntem çağrıldığında adını listeye yazar,
  dolayısıyla ölçüm bir çıkarım değil bir sayımdır.
- **DT3** — `Izci` üç öğeyle kurulur; döngü ölçümünde okunacak çağrı sayısı bu üçe bağlıdır.
- **DT4** — `__exit__` her koşumda `False` döndürür; bağlam bloğunda doğan istisna yutulmaz
  ve ölçüme, önüne bir ünlem işareti konmuş bir istisna adı olarak girer.
- **DT5** — Ölçüm hiçbir ortam verisi okumaz: bellek adresi, nesne kimliği sayısı ve süre
  kullanılmaz; sayılan tek şey çağrılan yöntem adlarıdır.
- **DT6** — Her biçim iki kez ölçülür — tablo için bir, çift karşılaştırması için bir.
  `Izci` her ölçümde yeniden kurulduğundan iki koşum aynı diziyi verir.

## Ölçüm

```python
print(f"{'sözdizim':<20s} {'çağrı':>6s}  protokol dizisi")
for ad, islev in BICIMLER:
    c = olc(islev)
    print(f"{ad:<20s} {len(c):6d}  {' '.join(c)}")

print()
ogeli = {ad: olc(f) for ad, f in BICIMLER}
tekil = sorted({p for c in ogeli.values() for p in c if not p.startswith("!")})
print(f"ayrı protokol {len(tekil)}: {', '.join(tekil)}")
print(f"toplam çağrı {sum(len(c) for c in ogeli.values())}, "
      f"istisnayla biten biçim "
      f"{sum(1 for c in ogeli.values() if any(p.startswith('!') for p in c))}")

print()
print("aynı görünen, ayrı protokol çağıran çiftler:")
for a, b in (("n + m", "n += m"),
             ("if n  (__len__)", "if n  (__bool__)")):
    print(f"  {a:<18s} -> {' '.join(ogeli[a])}")
    print(f"  {b:<18s} -> {' '.join(ogeli[b])}")

print()
dongu_kaydi = ogeli["for x in n"]
print(f"üç öğelik nesnede döngü: {dongu_kaydi.count('__next__')} kez __next__ "
      f"çağrıldı — öğe sayısından bir fazla; sonuncusu StopIteration ile bitti")
```

```
sözdizim              çağrı  protokol dizisi
for x in n                5  __iter__ __next__ __next__ __next__ __next__
x in n                    1  __contains__
if n  (__len__)           1  __len__
if n  (__bool__)          1  __bool__
n + m                     1  __add__
n += m                    1  __iadd__
n[0:2]                    1  __getitem__
str(n)                    1  __str__
with n                    2  __enter__ __exit__
with n + istisna          3  __enter__ __exit__ !ValueError
n == m                    1  __eq__

ayrı protokol 12: __add__, __bool__, __contains__, __enter__, __eq__, __exit__, __getitem__, __iadd__, __iter__, __len__, __next__, __str__
toplam çağrı 18, istisnayla biten biçim 1

aynı görünen, ayrı protokol çağıran çiftler:
  n + m              -> __add__
  n += m             -> __iadd__
  if n  (__len__)    -> __len__
  if n  (__bool__)   -> __bool__

üç öğelik nesnede döngü: 4 kez __next__ çağrıldı — öğe sayısından bir fazla; sonuncusu StopIteration ile bitti
```

## Sayıların Okunması

On bir biçim **12 ayrı protokolü** ve toplam **18 çağrıyı** üretiyor. Bu, kursun taşıyacağı
ölçüdür: bir dersin sayısı, anlattığı sözdizimin hangi protokolü kaç kez çağırdığıdır.
Tablonun her satırı bir sözdizim biçimini, karşısındaki dizi de o biçimin altında gerçekten
çalışan çağrıları gösteriyor. Hiçbiri tahmin değil; hepsi nesnenin kendi kaydından okundu.

**Aynı görünen iki ifade ayrı protokol çağırır.** `n + m` ile `n += m` yazımca birbirinin
kısaltması gibi durur, ama biri `__add__`, öteki `__iadd__` çağırıyor. Fark yalnız ad farkı
değildir: `__add__` **yeni bir nesne** üretip döndürür, `__iadd__` **var olan nesneyi
değiştirip** kendisini döndürür. Aynı biçimde `if n`, `Izci` üzerinde `__len__`, `Bosluksuz`
üzerinde `__bool__` çağırıyor — tek bir sözdizim, iki ayrı yöntem. Ayrımı yaratan şey
nesnenin hangi yöntemi tanımladığıdır. **Sözdizimin görünüşü çağrılan protokolü söylemez.**
Bu iddianın ilk yarısı bu konunun işleçler dersinde, ikinci yarısı akış konusunun koşullar
dersinde ödenecek.

**İstisna bir hata değil, protokolün parçasıdır.** Üç öğelik nesnede döngü **4 kez**
`__next__` çağırdı — öğe sayısından bir fazla. Dördüncü çağrı bir değer döndürmez,
`StopIteration` fırlatır; döngüyü bitiren şey tam olarak budur. `for` sözcüğü öğelerin
bittiğini saymayla değil, bir istisnayı yakalamayla anlar. `with n + istisna` satırı aynı
sınıftandır: blok içinde bir istisna doğduğu hâlde `__exit__` **yine çağrılmıştır** ve
kayıtta üçüncü öğe olarak istisnanın adı durur. `__exit__` `False` döndürdüğü için istisna
dışarı çıktı — dönseydi `True`, çıkmayacaktı.

## Protokol Yoksa Sözdizim de Yoktur

Yukarıdaki tablo `Izci`'nin yöntemleri **olduğu için** doludur. Aynı biçimler hiçbir özel
yöntem tanımlamayan bir nesne üzerinde denenirse ne olur? Ölçümün varsayımı:

- **DT7** — Karşılaştırılan nesne hiçbir özel yöntem tanımlamaz; taşıdığı tek şey her
  nesnenin tabanından gelen tanımlardır. Düşen biçimler istisna türüyle, düşmeyenler
  "çalıştı" ile yazılır; **değerleri basılmaz**, çünkü taban tanımın ürettiği metin ortama
  bağlı bir kimlik taşır.

```python
class Ciplak:
    """Hicbir ozel yontem tanimlamaz; nesnenin tabanindan geleni tasir."""


def ciplak_dongu():
    for _ in Ciplak():
        pass


def ciplak_baglam():
    with Ciplak():
        pass


CIPLAK_BICIMLER = (
    ("for x in n", ciplak_dongu),
    ("x in n", lambda: 9 in Ciplak()),
    ("n + m", lambda: Ciplak() + Ciplak()),
    ("n[0:2]", lambda: Ciplak()[0:2]),
    ("len(n)", lambda: len(Ciplak())),
    ("with n", ciplak_baglam),
    ("str(n)", lambda: str(Ciplak())),
    ("n == m", lambda: Ciplak() == Ciplak()),
)

print("özel yöntemi olmayan nesnede aynı biçimler:")
dusen = 0
for ad, islev in CIPLAK_BICIMLER:
    try:
        islev()
    except Exception as e:
        dusen += 1
        print(f"  {ad:12s} {type(e).__name__}")
    else:
        print(f"  {ad:12s} çalıştı — taban nesneden gelen tanım kullanıldı")
print(f"düşen biçim {dusen} / {len(CIPLAK_BICIMLER)}")
```

```
özel yöntemi olmayan nesnede aynı biçimler:
  for x in n   TypeError
  x in n       TypeError
  n + m        TypeError
  n[0:2]       TypeError
  len(n)       TypeError
  with n       TypeError
  str(n)       çalıştı — taban nesneden gelen tanım kullanıldı
  n == m       çalıştı — taban nesneden gelen tanım kullanıldı
düşen biçim 6 / 8
```

Sekiz biçimin **6**'sı düşüyor ve hepsi aynı istisnayla: `TypeError`. Adı yerindedir —
kusur, kullanılan değerde değil, nesnenin **o sözdizimi karşılayacak yöntemi
taşımamasındadır**. Kaynak metin kusursuzdur ve derleme aşamasını sorunsuz geçmiştir; iş
yürütmeye geldiğinde çağrılacak yöntem bulunamaz.

Düşmeyen **2** biçim de aynı kuralın öbür yüzüdür. `str(n)` ile `n == m` çalışıyor, çünkü
her nesne bu iki yöntemi **tabanından** devralır: birincisi nesneyi tanıtan bir metin
üretir, ikincisi eşitliği kimlik üzerinden yanıtlar. Yani protokol yine vardır; yalnız
tanımı sınıfın kendisinden değil, tabanından gelir. Bir sınıf `__str__` yazdığında sıfırdan
bir yetenek eklemez, devraldığı tanımın **yerine geçer**.

Sonuç, kursun taşıyacağı dördüncü okumadır: Python'da bir nesnenin "ne yapabildiği" tipinin
adıyla değil, **erişebildiği özel yöntemlerle** belirlenir. `__iter__` erişilebilirse
üzerinde döngü kurulur, `__len__` erişilebilirse uzunluğu sorulur; ikisi arasında bir
zorunluluk yoktur. Dil, bir biçimi çalıştırmadan önce tipin adına bakmaz — yöntemi arar,
bulamazsa `TypeError` fırlatır.

## Özet

- Kaynak metin önce sözcük birimlerine ve soyut sözdizim ağacına, sonra bayt koda çevrilir;
  yorumlayıcının yürüttüğü şey bayt koddur.
- Derleme dosyanın tamamı üzerinde yapılır: sözdizim kusuru hiçbir deyimi çalıştırmaz, ad
  kusuru ise ancak kendi sırası gelince ortaya çıkar ve öncekiler çalışmış olur.
- Sözdizim bir kısaltmadır; her biçim adı belli bir özel yöntemi çağırır ve yöntem kümesine
  protokol denir.
- On bir biçim **12 ayrı protokolü** toplam **18 çağrıyla** kullanır; `for x in n` tek başına
  5 çağrı üretir.
- Üç öğelik nesnede `__next__` **4 kez** çağrılır ve sonuncusu `StopIteration` fırlatır;
  istisna burada bir kusur değil, döngüyü bitiren düzenektir.
- Hiçbir özel yöntem tanımlamayan bir nesnede aynı sekiz biçimin **6**'sı `TypeError` ile
  düşer; kalan **2**'si taban nesneden devralınan tanımla çalışır.

## Sonraki Adım

Bu derste sözdizimin altındaki çağrılar sayıldı, ama sözdizimin kendisi hiç sorgulanmadı:
`for` satırının altındaki gövdenin nereden başlayıp nerede bittiğini ne belirliyor? Python
bunu ayraçla değil **boşlukla** yapar ve boşluk burada bir yazım tercihi değil, dilbilgisinin
parçasıdır. Sonraki ders bunu ölçer: aynı sözcük birimi dizisi, yalnızca girintisi
değiştirilerek farklı bir ağaca ve farklı bir sonuca nasıl dönüşüyor — ve bir deyim ile bir
ifade arasındaki sınır tam olarak nerede?
