---
title: 'Statik Tip Denetimi'
source: 'https://academia.sh/tr/kurslar/python-nesne-ve-tipler/statik-tip-denetimi'
course: 'Nesneye Dayalı Python ve Tipler'
language: tr
updated: '2026-08-17T18:10:30+00:00'
license: 'CC BY-SA 4.0'
---

# Statik Tip Denetimi

Ders içinde modellenen denetleyici beş çağrının iki tip ihlalini bulur ve iki alan ihlalini göremez; buna karşılık kaynaktaki üç çağrı yerinin üçünü de görür, oysa çalışma zamanı yalnız birine uğrar.

Önceki ders açıklamayı okuyan tarafın tek bir adımını modelledi: bir çağrı yerinin dönüş
tipini çıkarmak. Ölçüm, genellikli yazımın iki ihlali de yanlış alarmsız bildirebildiğini
gösterdi — ama bildirimi yapan bir program yoktu, yalnız bir çıkarım kuralı vardı.

Bu ders o programı kurar. Kaynağı ya da açıklamaları okuyup çağrıları imzayla karşılaştıran
ve uyuşmayanları **çalıştırmadan** bildiren bir katmana **statik tip denetimi (static type
checking)** denir. Sorulan sayı üç parçalıdır: bu katman ilk dersteki dört ihlalin kaçını
bulur, kaçını bulamaz, ve bulamadıklarını neden bulamaz?

## Denetleyici Bu Derste Modellenmiştir

Bir uyarı baştan: bu derste kullanılan denetleyici **ders içinde yazılmıştır**. Hazır bir
araç çalıştırılmaz ve hiçbir aracın adı geçmez. Ölçülen şey belirli bir programın davranışı
değil, **yaklaşımın kendisinin sınırıdır** — ve o sınır, denetleyicinin elindeki bilgiden
çıkar. Modellenmiş denetleyici o bilgiyi aynen kullanır: açıklamaları okur, çağrının
argümanlarına bakar, uyuşmazlıkları listeler.

Ölçüm iki denetleyici biçimi kullanıyor. Birincisi çağrıların argümanlarını doğrudan alır;
argümanlar kaynağa yazılmış sabitler olduğu için bu, kaynağı okumakla aynı bilgiyi verir.
İkincisi işi daha açık yapar: kaynak metni bir **soyut sözdizim ağacına (abstract syntax
tree)** çevirir, ağaçtaki çağrı düğümlerini gezer ve sabit argümanları imzayla
karşılaştırır. İkincisi kodu **hiç çalıştırmaz**; bu, statik sözcüğünün ölçülebilir
karşılığıdır.

## Denetleyicinin Elindeki Bilgi

Denetleyici üç şeyi görür: imzadaki açıklamaları, çağrı yerlerini ve çağrılara yazılmış
sabit değerleri. Görmediği tek bir şey vardır ve bütün ders o şeyin sonuçlarıdır:
**değerin anlamını** görmez.

`yas: int` açıklaması "buraya bir tam sayı gelir" der. `-5` bir tam sayıdır. Denetleyici
`isinstance(-5, int)` sınamasını yapar, **doğru** yanıtını alır ve geçer. Aynı biçimde
`ad: str` açıklaması boş dizgiyi dışlamaz, çünkü boş dizgi bir dizgidir. Bu bir eksiklik
değil, imzada yazanın tam karşılığıdır: **imza tipi bildirir, alanı bildirmez.**

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

- **TP18** — Kâhin kurgunun kendisidir: beş çağrının hangisinin tip, hangisinin alan ihlali
  taşıdığı ilk derste belirlendi ve burada değiştirilmedi.
- **TP19** — Denetleyici ders içinde modellenmiştir; hiçbir dış araç çalıştırılmaz.
  Modelin kuralı tektir: açıklamadaki tiple bağlanan değerin tipi uyuşmuyorsa bildir.
- **TP20** — Alan kuralı da ders içinde modellenmiştir ve yalnız iki koşula bakar: ad boş
  mu, yaş negatif mi. Bu iki koşul kurgunun alan ihlali tanımıdır.
- **TP21** — Kapsam ölçümünde denetlenen kod bir metin sabiti olarak durur; hem soyut
  sözdizim ağacına çevrilerek denetlenir hem de çalıştırılır. İki katman **aynı metne**
  uygulanır.
- **TP22** — "Çalıştırılan çağrı yeri", koşum sırasında gerçekten uğranan çağrı sayısıdır
  ve izlenen bir sarmalayıcıyla sayılır. Koşum tek bir kiple yapılır.
- **TP23** — Soyut sözdizim ağacı denetimi yalnız **sabit** argümanları sınar; değişkenle
  verilen argüman bu modelde denetlenmez ve sayılmaz.
- **TP24** — Alan tipi ölçümünde `Yas` sınıfı tam sayıdan türetilir ve kuralı **kurulum
  anında** sınar; sınıfın örneği her yerde bir tam sayı gibi davranır.
- **TP25** — "Ham çağrıda bildirilen", kaynağa yazıldığı hâliyle beş çağrıdan kaçının
  bildirildiğidir; sayılan çağrıdır, parametre değil.
- **TP26** — "Kurulum sırasında hata veren", değerin `Yas` üzerinden kurulmaya
  çalışıldığında istisna fırlatan çağrı sayısıdır.
- **TP27** — Ortama bağlı hiçbir veri yazılmaz; satır numaraları denetlenen metin
  sabitinin kendi satırlarıdır.

## Ölçüm

```python
"""Statik tip denetimi: modellenmis denetleyici neyi bulur, neyi goremez."""
import ast


def yas_yaz(ad: str, yas: int) -> str:
    return f"{ad}:{yas}"


CAGRILAR = (
    ("ad", 30),            # uygun
    ("ad", "otuz"),        # tip ihlali: yas dizgi
    (7, 30),               # tip ihlali: ad sayi
    ("ad", -5),            # tip uygun, alan ihlali: yas negatif
    ("", 30),              # tip uygun, alan ihlali: ad bos
)


def denetleyici(islev, cagrilar):
    """Aciklamalari okuyup cagrilari denetleyen modellenmis denetleyici.

    Yalnizca TIP uyusmazligini gorur; degerin anlamini goremez.
    """
    aciklama = [(a, t) for a, t in islev.__annotations__.items() if a != "return"]
    bulunan = []
    for i, arg in enumerate(cagrilar):
        for (ad, tip), deger in zip(aciklama, arg):
            if not isinstance(deger, tip):
                bulunan.append((i, ad))
    return bulunan


def alan_kurali(ad, yas):
    """Tipin goremedigi anlam kosullari."""
    hatalar = []
    if isinstance(ad, str) and not ad:
        hatalar.append("ad bos")
    if isinstance(yas, int) and yas < 0:
        hatalar.append("yas negatif")
    return hatalar


bulunan = denetleyici(yas_yaz, CAGRILAR)
tip_ihlali = [i for i, a in enumerate(CAGRILAR)
              if not isinstance(a[0], str) or not isinstance(a[1], int)]
alan_ihlali = [i for i, a in enumerate(CAGRILAR) if alan_kurali(*a)]

print(f"{'çağrı':>6s} {'tip ihlali':>11s} {'alan ihlali':>12s} "
      f"{'denetleyici buldu':>18s} {'çalışma zamanı':>15s}")
for i, arg in enumerate(CAGRILAR):
    print(f"{i:6d} {str(i in tip_ihlali):>11s} {str(i in alan_ihlali):>12s} "
          f"{str(any(b[0] == i for b in bulunan)):>18s} {'hata yok':>15s}")
print(f"çağrı {len(CAGRILAR)}, tip ihlali {len(tip_ihlali)}, alan ihlali "
      f"{len(alan_ihlali)}, denetleyicinin bulduğu {len({b[0] for b in bulunan})}")
print(f"denetleyicinin gösterdiği parametreler: {bulunan}")

KAYNAK = '''
def dagit(kip):
    if kip == "kisa":
        return yas_yaz("ad", 30)
    if kip == "uzun":
        return yas_yaz("ad", "otuz")
    return yas_yaz(7, 30)
'''


def statik_denetle(kaynak, islev):
    """Kaynak metni okuyup cagrilari denetleyen modellenmis denetleyici.

    Kodu calistirmaz; yalnizca soz dizim agacini gezer.
    """
    imza = [(a, t) for a, t in islev.__annotations__.items() if a != "return"]
    yerler, bulunan = [], []
    for dugum in ast.walk(ast.parse(kaynak)):
        if isinstance(dugum, ast.Call) and getattr(dugum.func, "id", "") == "yas_yaz":
            yerler.append(dugum.lineno)
            for arg, (ad, tip) in zip(dugum.args, imza):
                if isinstance(arg, ast.Constant) and not isinstance(arg.value, tip):
                    bulunan.append((dugum.lineno, ad))
    return yerler, bulunan


GECILEN = []


def izlenen(ad, yas):
    GECILEN.append((ad, yas))
    return yas_yaz(ad, yas)


ortam = {"yas_yaz": izlenen}
exec(KAYNAK, ortam)
hata = 0
try:
    ortam["dagit"]("kisa")
except Exception:
    hata += 1

yerler, statik = statik_denetle(KAYNAK, yas_yaz)
print()
print(f"kaynaktaki çağrı yeri {len(yerler)}, çalıştırılan {len(GECILEN)}, "
      f"çalışma zamanında hata {hata}")
print(f"denetleyicinin gördüğü çağrı yeri {len(yerler)}, bulduğu ihlal "
      f"{len({y for y, _ in statik})}, bulduğu yerler {statik}")

KAYNAK2 = '''
def yolla(deger):
    return yas_yaz("ad", deger)
'''
yerler2, statik2 = statik_denetle(KAYNAK2, yas_yaz)
print(f"değişkenle yazılan çağrı: görülen yer {len(yerler2)}, "
      f"sınanan argüman 0, bildirilen {len(statik2)}")


class Yas(int):
    """Alan kuralini tipe tasiyan deger nesnesi: kurulum sinar."""

    def __new__(cls, deger):
        if deger < 0:
            raise ValueError("yas negatif")
        return super().__new__(cls, deger)


def yas_yaz_alan(ad: str, yas: Yas) -> str:
    return f"{ad}:{yas}"


ham = len({b[0] for b in denetleyici(yas_yaz_alan, CAGRILAR)})
kurulan, kurulum_hatasi = [], 0
for a, y in CAGRILAR:
    try:
        kurulan.append((a, Yas(y)))
    except (ValueError, TypeError):
        kurulum_hatasi += 1
kurulu = len({b[0] for b in denetleyici(yas_yaz_alan, kurulan)})
print()
print(f"alan tipli imza: ham çağrıda bildirilen {ham}, kurulum sırasında hata veren "
      f"{kurulum_hatasi}, kurulan {len(kurulan)} çağrıda bildirilen {kurulu}")
print(f"Yas(30) bir tam sayı mı: {isinstance(Yas(30), int)}; "
      f"boş ad hâlâ görünmüyor: {denetleyici(yas_yaz_alan, [('', Yas(30))])}")
```

```
 çağrı  tip ihlali  alan ihlali  denetleyici buldu  çalışma zamanı
     0       False        False              False        hata yok
     1        True        False               True        hata yok
     2        True        False               True        hata yok
     3       False         True              False        hata yok
     4       False         True              False        hata yok
çağrı 5, tip ihlali 2, alan ihlali 2, denetleyicinin bulduğu 2
denetleyicinin gösterdiği parametreler: [(1, 'yas'), (2, 'ad')]

kaynaktaki çağrı yeri 3, çalıştırılan 1, çalışma zamanında hata 0
denetleyicinin gördüğü çağrı yeri 3, bulduğu ihlal 2, bulduğu yerler [(7, 'ad'), (6, 'yas')]
değişkenle yazılan çağrı: görülen yer 1, sınanan argüman 0, bildirilen 0

alan tipli imza: ham çağrıda bildirilen 5, kurulum sırasında hata veren 2, kurulan 3 çağrıda bildirilen 1
Yas(30) bir tam sayı mı: True; boş ad hâlâ görünmüyor: []
```

## İki Bulunan, İki Bulunamayan

Üst tablo dersin bağlayıcı sayısını veriyor. Beş çağrının **2**'si tip ihlali, **2**'si alan
ihlali taşıyor. Denetleyicinin bulduğu çağrı sayısı **2** ve bulduğu çağrılar tam olarak tip
ihlali taşıyanlar: birinci çağrıda `yas`, ikinci çağrıda `ad`. Alan ihlali taşıyan iki
çağrıda sütun **yanlış** diyor.

**Kim yakaladı:** bu ölçümde iki ihlali **denetleyici** yakaladı, iki ihlali **hiçbir katman
yakalamadı**. Çalışma zamanı sütunu beş satırda da temiz.

Bulunamayanların nedeni tek satırda okunabilir. Üçüncü çağrıda yaş **-5**'tir ve `-5` bir
tam sayıdır; `isinstance(-5, int)` doğru döner. Dördüncü çağrıda ad boş dizgidir ve boş
dizgi bir dizgidir; `isinstance("", str)` doğru döner. Denetleyici yanlış çalışmadı —
sorulan soruyu doğru yanıtladı. Sorulmayan soru "bu değer geçerli mi" sorusuydu ve imzada
o soru yazmıyor.

Buradan kursun ölçü ekseninin en keskin cümlesi çıkıyor: **tipin doğru olması değerin
geçerli olduğu anlamına gelmez.** Denetleyiciyi daha titiz yazmak bu iki ihlali bulmaz,
çünkü sorun titizlikte değil **bilgide**: denetleyicinin elinde `yas` alanının negatif
olamayacağını söyleyen hiçbir kayıt yok.

Denetleyicinin gösterdiği parametre listesi bu bilgi sınırının biçimini de veriyor.
Bildirim yalnız çağrı numarasını değil, **hangi parametrenin** uymadığını da taşıyor:
birinci çağrıda `yas`, ikinci çağrıda `ad`. Bildirim bu kadar somut olabiliyor, çünkü
imzada o parametrenin karşısında bir tip adı yazılı. Alan kuralının karşısında yazılı bir
şey olmadığı için üretilebilecek bir bildirim de yok.

## Kapsam Çalıştırılan Kod Değil, Yazılan Koddur

İkinci ölçüm denetleyicinin karşılığında ne verdiğini gösteriyor ve sayı çarpıcı.

Denetlenen metinde `yas_yaz` için **3** çağrı yeri var; ikisi ihlalli. Program tek bir kiple
çalıştırıldığında bu üç yerden yalnız **1**'ine uğranıyor, çünkü öbür ikisi sağlanmayan
koşulların içinde. Çalışma zamanında hata sayısı **0** — hem uğranan çağrı temiz olduğu
için, hem de önceki derste ölçüldüğü gibi açıklama zaten çağrıyı durdurmadığı için.

Soyut sözdizim ağacını gezen denetleyici ise **3** çağrı yerinin üçünü de görüyor ve **2**
ihlal bildiriyor; satır numaralarıyla birlikte, hangi parametrenin uymadığını da
söyleyerek. Kodu hiç çalıştırmadı: `ast.parse` metni bir ağaca çevirdi, `ast.walk` ağacı gezdi, hiçbir gövde
yürütülmedi.

Aradaki fark bir yüzde değil, bir **cins** farkı. Çalışma zamanı ancak uğradığı yeri
inceler; uğrama koşullara, girdiye ve o koşumdaki kipe bağlıdır. Denetleyicinin kapsamı ise
metnin kendisidir: bir kez yazılmış her çağrı yeri, hiç çalıştırılmasa bile denetlenir.
Ölçümdeki oran **3/3**'e karşı **1/3**'tür.

Oranın anlamı koşuma göre değişir. Aynı program üç ayrı kiple çalıştırılsaydı üç çağrı
yerinin üçüne de uğranır ve ikinci ile üçüncü çağrılar yine hata vermezdi — çünkü açıklama
çağrıyı durdurmaz. Yani **1/3** sayısı denetimin değil, kapsamın ölçüsüdür: hangi kod
parçasına bakıldığını söyler, bakılanın doğru bulunup bulunmadığını değil.

Bu, denetleyicinin bulamadığı iki ihlalin bedelini karşılamaz ama onunla takas edilir.
Denetleyici **daha az şey** bilir ve o azı **daha çok yerde** bilir; çalışma zamanı **daha
çok şey** bilir — elindeki gerçek değeri görür — ve onu **daha az yerde** bilir.

## Modelin Kendi Sınırı

Bir satır bu tablonun altında duruyor ve dürüstlük gereği ayrı sayılmalı. Argümanı bir
sabit değil bir **değişken** olan çağrıda denetleyici çağrı yerini görüyor — **1** — ama
sınadığı argüman **0** ve bildirdiği ihlal **0**.

Bu, önceki bölümdeki sınırla aynı cinsten değil. Alan ihlalini bulamamak bir **bilgi**
sınırıydı: kural hiçbir yerde yazılı olmadığı için hiçbir denetleyici onu okuyamaz.
Değişkenli çağrıyı sınayamamak ise bir **model** sınırıdır: değişkenin hangi değeri
taşıdığı, ona yapılan atamalar izlenerek çıkarılabilir. Bu derste modellenen denetleyici o
izlemeyi yapmaz ve sınırı burada açıkça yazılır.

Ayrım önemli, çünkü ikisinin çaresi ayrı. Model sınırı **daha iyi bir denetleyiciyle**
kapanır. Bilgi sınırı kapanmaz — kuralı bir yere yazmadan hiçbir denetleyici onu bulamaz.
Bir sonraki bölüm o "bir yer"in ne olabileceğini deniyor.

## Alan Kuralını Tipe Taşımak

Üçüncü ölçüm doğal bir soruyu deniyor: denetleyicinin elinde alan bilgisi yoksa, o bilgiyi
imzaya **koysak** ne olur? Ölçüm tam sayıdan türeyen bir `Yas` sınıfı kuruyor; sınıf
kuralını kurulum anında sınıyor ve örneği her yerde bir tam sayı gibi davranıyor — son satır
bunu `isinstance` ile gösteriyor.

İmza `yas: Yas` olduğunda denetleyici gerçekten değişiyor. Kaynağa yazıldığı hâliyle beş
çağrının **5**'i bildiriliyor: hiçbir sabit sayı bir `Yas` örneği değildir, temiz çağrı bile
değildir. Yani alan bilgisini imzaya koymak, **her çağrı yerini değeri kurmaya zorluyor.**

Değerler kurulduğunda tablo yerine oturuyor. Beş çağrının **2**'si kurulum sırasında hata
veriyor — negatif yaş ve tam sayı olmayan yaş, ikisi de `Yas` yapıcısında düşüyor. Geriye
kalan **3** çağrıdan **1**'i denetleyici tarafından bildiriliyor: adı sayı olan çağrı.

Ama son satır kalan boşluğu gösteriyor: boş ad hâlâ görünmüyor, çünkü `ad` için böyle bir
tip kurulmadı. Dört ihlalden üçü artık yakalanmış durumda ve yakalayanlar ayrı katmanlar —
**1**'ini denetleyici, **2**'sini kurulum.

Buradan çıkan sonuç dersin sonucudur ve bir sonrakinin başlangıcı. Alan kuralını tipe
taşımak ihlali görünür kılar, ama **görünür kıldığı yer denetleyici değil çalışma
zamanıdır**: `Yas(-5)` çağrısı bir istisna fırlatır ve bu istisna program çalışırken çıkar.
Kural yazıya değil, bir yapıcının gövdesine taşınmıştır.

## Özet

- Statik tip denetimi, kodu çalıştırmadan okuyup çağrıları imzayla karşılaştıran bir
  katmandır; bu derste denetleyici ders içinde modellenmiştir ve hiçbir dış araç
  kullanılmamıştır.
- Denetleyici beş çağrıda **2** tip ihlalini bulur ve **2** alan ihlalini göremez. Nedeni
  bilgi eksikliğidir: `-5` bir tam sayıdır, boş dizgi bir dizgidir ve imzada alan kuralı
  yazmaz.
- Denetimin kapsamı yazılan koddur: soyut sözdizim ağacını gezen denetleyici **3** çağrı
  yerinin üçünü de görüp **2** ihlal bildirir, oysa koşum yalnız **1** çağrı yerine uğrar ve
  **0** hata verir.
- Denetleyici daha az şeyi daha çok yerde, çalışma zamanı daha çok şeyi daha az yerde bilir;
  ikisi birbirinin yerine geçmez.
- İki sınır ayrı cinstendir: değişkenli çağrıyı sınayamamak bir model sınırıdır ve daha iyi
  bir denetleyiciyle kapanır; alan kuralını görememek bir bilgi sınırıdır ve kural bir yere
  yazılmadan kapanmaz.
- Alan kuralı tipe taşındığında ham çağrıların **5**'i bildirilir, **2**'si kurulum
  sırasında hata verir, kurulan üç çağrıdan **1**'i denetleyiciye takılır — kural yazıdan
  bir yapıcının gövdesine, yani çalışma zamanına taşınmıştır.

## Sonraki Adım

Üçüncü ölçüm kuralı çalışma zamanına taşıdı ama bunu tek bir alan için, elle yazılmış bir
sınıfla yaptı. Her alan için ayrı bir sınıf yazmak ölçeklenmez; kural sayısı arttıkça
yapıcılar birbirini tekrar eder ve kuralların nerede durduğu dağılır. Kuralları tek bir
yerde, verinin **şeması** olarak toplamak ve bu şemayı çağrı anında uygulamak da mümkündür.
Sonraki ders bu katmanı ders içinde modelliyor ve kursun son sayısını soruyor: çalışma
zamanı, denetleyici ve alan kuralı ayrı ayrı kaç ihlal yakalar — ve dört ihlalin tamamını
görmek için kaç katman gerekir?
