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

# Tip İpuçları

Açıklama koda yazılır, yorumlayıcı onu saklar ve sınamaz: beş çağrının dördü ihlallidir ve çalışma zamanında hata veren sayısı sıfırdır, ama açıklamaların tamamı çalışma zamanında okunabilir.

Önceki ders sözleşmeyi kodda bildirdi ve sınadı. Soy bağı arayan sınama bir sınıfın
bildirilmiş atasını sordu, yöntem arayan sınama nesnenin gerçekten taşıdığı adlara baktı;
ikisi de bir **çalıştırma** sırasında, elde duran nesne üzerinde sonuç verdi. Sözleşme
koddaydı ve sınayan da koddu.

Bu dersin sorusu sözleşmenin başka bir yazılış biçiminden doğar. Bir işlevin imzasına
"buraya bir dizgi gelir, buradan bir tam sayı çıkar" yazmak da bir sözleşmedir — ama bu
sözleşme bir sınıf ağacına ya da bir yöntem adına değil, **yazının kendisine** işlenmiştir.
Kursun ekseni burada da aynı: **sözü kim denetler?** Bu ders yanıtın bir katmanını ölçer,
en alttakini: çalışma zamanı.

## Açıklamanın Yazılışı

**Tip ipucu (type hint)** bir adın hangi tipte değer taşımasının beklendiğini bildiren
sözleşmedir. **Açıklama (annotation)** o ipucunun koda yazılmış hâlidir: işlev
parametresinden sonra iki nokta ve tip, dönüş için `->` ve tip.

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

Aynı yazım işlev dışında da geçerlidir. Bir sınıf gövdesinde `ad: str` bir alanı bildirir,
modül düzeyinde `sayac: int = 0` bir değişkeni bildirir. Dördüncü bir yer daha vardır: bir
işlevin gövdesindeki yerel değişken. Ölçüm bu dördünü ayırıyor, çünkü yazımın aynı olması
sonucun aynı olduğu anlamına gelmiyor.

Programlama Temelleri kursu tip dönüşümünü ve dinamik tiplemeyi kurdu; bir adın çalışma
zamanında hangi nesneye bağlandığı orada ölçüldü ve burada tekrarlanmaz. **TypeScript**
kursu da aynı soruyu, tip katmanı çalıştırmadan önce denetlenen bir dilde sordu. Buradaki
fark tek cümleyle şudur: Python'da açıklama **çalıştırmayı durdurmaz**; bu dersin ölçtüğü
şey tam olarak o durdurmamanın sayısıdır.

## Yorumlayıcı Açıklamayla Ne Yapar

Yorumlayıcı açıklamayı okur, değerlendirir ve **saklar**. Sakladığı yer işlevin
`__annotations__` sözlüğüdür; sınıfın ve modülün de aynı adlı bir sözlüğü olur. Bu sözlük
sıradan bir sözlüktür ve çalışma zamanında okunabilir.

Yapmadığı şey ise sınamaktır. Çağrı geldiğinde parametreye bağlanan nesnenin açıklamaya
uyup uymadığına bakılmaz; uymayan bir nesne de bağlanır, işlev gövdesi de çalışır. Bunun
doğrudan bir sonucu vardır: açıklamaya yazılan şeyin bir **tip** olması bile gerekmez.
Ölçüm bunu da gösteriyor — dönüş açıklaması olarak bir sayı yazılmış bir işlev sorunsuz
tanımlanır, sorunsuz çağrılır ve o sayı sözlükte olduğu gibi durur.

Ölçüm iki soruyu ayrı ayrı soruyor. Birincisi: açıklama nereye yazılırsa saklanır?
İkincisi: açıklamaya uymayan çağrılar çalıştırıldığında kaçı hata verir?

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

- **TP1** — Kâhin kurgunun kendisidir: beş çağrının hangisinin tip ihlali, hangisinin alan
  ihlali taşıdığını dersin yazarı belirledi. Ölçüm bu sınıflandırmayı bir araçtan değil,
  kurgudan alır.
- **TP2** — **Tip ihlali**, bağlanan nesnenin açıklamadaki tipten olmamasıdır. **Alan
  ihlali (domain violation)**, nesnenin tipi doğru olduğu hâlde değerinin alan kuralını
  bozmasıdır: boş ad, negatif yaş.
- **TP3** — Beş çağrının ikisi tip ihlali, ikisi alan ihlali taşır, biri temizdir. Diziler
  bu kursun ortak kurgusundan gelir ve derste değiştirilmez.
- **TP4** — Çalışma zamanı katmanının ölçüsü, çağrının bir istisna fırlatıp fırlatmadığıdır.
  Gövde bir dizgi biçimlemesinden ibarettir ve biçimleme her tipi kabul eder; ölçülen şey
  gövdenin ne yaptığı değil, **açıklamanın çağrıya karışıp karışmadığıdır**.
- **TP5** — Saklama ölçümünde sayılan şey `__annotations__` sözlüğünün öğe sayısıdır; dönüş
  açıklaması da bir öğedir.
- **TP6** — Yerel değişken açıklaması için "hiçbir yerde" denmesi, işlev nesnesinde böyle
  bir kaydın bulunmaması demektir; ölçü `__annotations__` üzerinden alınır.
- **TP7** — Bileşik açıklama ölçümünde sorulan tek soru, saklanan nesnenin `isinstance`
  sınamasına doğrudan verilip verilemeyeceğidir. Sınama başarısız olursa değil, sınamanın
  kendisi bir istisna fırlatırsa "hayır" yazılır.
- **TP8** — Son satırdaki hata, açıklamanın değil **gövdenin** ürettiği hatadır; ölçüm bu
  ikisini ayırmak için o çağrıyı ayrıca çalıştırır.

## Ölçüm

```python
"""Tip ipuclari: aciklama nerede saklanir, calisma zamaninda ne denetlenir."""

sayac: int = 0


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


class KayitAlani:
    ad: str
    yas: int = 0


def yerel_aciklama():
    gecici: int = 1
    return gecici


def saklanan(nesne):
    return len(getattr(nesne, "__annotations__", {}))


print(f"{'açıklamanın yeri':<22s} {'saklandığı yer':<24s} {'okunabilen':>10s}")
YERLER = (("işlev imzası", "islev.__annotations__", saklanan(yas_yaz)),
          ("sınıf gövdesi", "Sinif.__annotations__", saklanan(KayitAlani)),
          ("modül düzeyi", "modül __annotations__", len(__annotations__)),
          ("işlev gövdesi", "hiçbir yerde", saklanan(yerel_aciklama)))
for yer, nerede, sayi in YERLER:
    print(f"  {yer:<20s} {nerede:<24s} {sayi:10d}")
print(f"işlev açıklaması: {yas_yaz.__annotations__}")


def deneme(x: "sayi olmayan bir aciklama") -> 3:
    return x


print(f"tip olmayan açıklama saklandı: {deneme.__annotations__}")
print(f"deneme(41) -> {deneme(41)}")

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 alan_kurali(ad, yas):
    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


def calisma_zamani(cagrilar):
    hata, sonuc = 0, []
    for arg in cagrilar:
        try:
            sonuc.append(yas_yaz(*arg))
        except Exception:
            hata += 1
            sonuc.append(None)
    return hata, sonuc


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)]
hata, sonuc = calisma_zamani(CAGRILAR)

print()
print(f"{'çağrı':>6s} {'tip ihlali':>11s} {'alan ihlali':>12s} "
      f"{'çalışma zamanı':>15s} {'dönen değer':>13s}")
for i, arg in enumerate(CAGRILAR):
    print(f"{i:6d} {str(i in tip_ihlali):>11s} {str(i in alan_ihlali):>12s} "
          f"{'hata yok':>15s} {sonuc[i]!r:>13s}")
print()
print(f"çağrı {len(CAGRILAR)}, ihlalli {len(set(tip_ihlali) | set(alan_ihlali))}, "
      f"tip ihlali {len(tip_ihlali)}, alan ihlali {len(alan_ihlali)}, "
      f"çalışma zamanında hata veren {hata}")


def toplam(sayilar: list[int]) -> int:
    return sum(sayilar)


BILESIK = (("list[int]", list[int], [1, 2]),
           ("str | None", str | None, "ad"),
           ("dict[str, int]", dict[str, int], {"a": 1}),
           ("tuple[int, ...]", tuple[int, ...], (1, 2)))

print()
print(f"{'bileşik açıklama':<18s} {'isinstance ile sınanır':>23s}")
for ad, nesne, ornek in BILESIK:
    try:
        isinstance(ornek, nesne)
        sinanir = "evet"
    except TypeError:
        sinanir = "hayır"
    print(f"  {ad:<16s} {sinanir:>23s}")
print(f"toplam açıklaması: {toplam.__annotations__}")
try:
    toplam(["a", "b"])
    print("toplam(['a', 'b']) -> hata yok")
except TypeError:
    print("toplam(['a', 'b']) -> gövde hata verdi, açıklama değil")
```

```
açıklamanın yeri       saklandığı yer           okunabilen
  işlev imzası         islev.__annotations__             3
  sınıf gövdesi        Sinif.__annotations__             2
  modül düzeyi         modül __annotations__             1
  işlev gövdesi        hiçbir yerde                      0
işlev açıklaması: {'ad': <class 'str'>, 'yas': <class 'int'>, 'return': <class 'str'>}
tip olmayan açıklama saklandı: {'x': 'sayi olmayan bir aciklama', 'return': 3}
deneme(41) -> 41

 çağrı  tip ihlali  alan ihlali  çalışma zamanı   dönen değer
     0       False        False        hata yok       'ad:30'
     1        True        False        hata yok     'ad:otuz'
     2        True        False        hata yok        '7:30'
     3       False         True        hata yok       'ad:-5'
     4       False         True        hata yok         ':30'

çağrı 5, ihlalli 4, tip ihlali 2, alan ihlali 2, çalışma zamanında hata veren 0

bileşik açıklama    isinstance ile sınanır
  list[int]                          hayır
  str | None                          evet
  dict[str, int]                     hayır
  tuple[int, ...]                    hayır
toplam açıklaması: {'sayilar': list[int], 'return': <class 'int'>}
toplam(['a', 'b']) -> gövde hata verdi, açıklama değil
```

## Saklanan ve Saklanmayan

Üst tablo dört yeri yan yana koyuyor ve üçü ile biri arasında keskin bir sınır var.

İşlev imzası **3** açıklama saklıyor: iki parametre ve dönüş. Sınıf gövdesi **2**, modül
düzeyi **1** saklıyor. Bu üç yerde açıklama, yazıldığı nesnenin bir özniteliğine dönüşüyor
ve program çalışırken oradan okunabiliyor.

İşlev gövdesindeki yerel açıklama ise **0** veriyor. `gecici: int = 1` satırı geçerli bir
Python satırıdır, atamayı yapar, hata vermez — ama geride bir kayıt bırakmaz. Yerel adın
ömrü çağrı ile sınırlıdır ve açıklamasının ömrü de o kadardır. Aynı işlev bin kez
çağrılsaydı bin ayrı yerel ad doğup ölecek, saklanan açıklama sayısı yine **0** kalacaktı.
Sınıf ve modül düzeyinde durum tersidir: oradaki açıklama bir kez, tanımın yapıldığı anda
işlenir ve nesnenin ömrü boyunca durur.

Buradan çıkan ayrım sonraki derslerin dayanağıdır: **kaynağa yazılan her açıklama çalışma
zamanında görünmez.** Kaynağı okuyan bir katman dört yerin dördünü de görür; çalışan
programı okuyan bir katman yalnız üçünü.

Tip olmayan açıklamanın satırı da aynı noktayı başka yönden vuruyor. Dönüş açıklaması
olarak yazılan `3` sözlükte `3` olarak duruyor ve `deneme(41)` çağrısı **41** döndürüyor.
Yorumlayıcı açıklamanın anlamlı olup olmadığına bakmıyor, çünkü açıklamayı yorumlayan bir
tarafı yok.

## Dört İhlal, Sıfır Hata

Alt tablo dersin ana sayısını veriyor. Beş çağrının **ikisi** tip ihlali taşıyor: birinde
yaş yerine bir dizgi, öbüründe ad yerine bir sayı geçiyor. **İkisi** alan ihlali taşıyor:
yaş **-5**, ad boş dizgi. Toplam **4** çağrı ihlallidir ve biri temizdir.

Çalışma zamanı sütunu beş satırın beşinde de "hata yok" diyor. Sayı **0**. Dördüncü sütun
bunun nedenini de gösteriyor: her çağrı bir değer döndürüyor. `'ad:otuz'` üretiliyor, çünkü
biçimleme bir dizgiyi de kabul eder. `'7:30'` üretiliyor, çünkü biçimleme bir sayıyı da
kabul eder. `'ad:-5'` ve `':30'` üretiliyor, çünkü **-5** bir tam sayıdır ve boş dizgi bir
dizgidir.

**Bu ölçümde ihlalleri yakalayan katman yok.** Kim yakaladı sorusunun yanıtı bu derste
tektir ve boştur: çalışma zamanı **0** yakalar. Kâhin dört ihlali biliyor çünkü kurguyu biz
yazdık; program bilmiyor.

Bir itiraz burada haklıdır: gövde bir dizgi biçimlemesinden ibaret olduğu için hoşgörülü;
başka bir gövde yanlış tiple hata verirdi. Doğrudur, ama ölçülen şey bu değil. Öyle bir
gövde hata verseydi hatayı veren yine **gövdedeki işlem** olurdu, açıklama değil — ve hata
işlev çağrıldıktan sonra, işin bir bölümü çoktan yapılmışken çıkardı. Açıklamanın
sağlayabileceği tek şey çağrının **hiç başlamamasıydı**; ölçümde başlamayan çağrı yok.

İki ihlal türünün ayrı sayılması bir ayrıntı değil, kursun geri kalanının ekseni.
`ad: str` açıklaması boş dizgiyi dışarıda bırakmaz — boş dizgi bir dizgidir. `yas: int`
açıklaması negatif sayıyı dışarıda bırakmaz — **-5** bir tam sayıdır. **Tipin doğru olması
değerin geçerli olduğu anlamına gelmez** ve bu, açıklamaların bir eksiği değil, tanımıdır:
açıklama tipi bildirir, değeri değil.

## Bileşik Açıklama Bir Sınama Değildir

Açıklama tek bir tip adından ibaret olmak zorunda değil. `list[int]` "tam sayı listesi",
`dict[str, int]` "anahtarları dizgi, değerleri tam sayı olan sözlük", `str | None` "dizgi
ya da hiçbiri", `tuple[int, ...]` "istenildiği kadar tam sayıdan oluşan demet" bildirir.
Dördü de geçerli açıklamadır ve dördü de `__annotations__` içinde bir nesne olarak durur —
son satır bunu gösteriyor: `toplam` işlevinin açıklaması `list[int]` nesnesini taşıyor.

Üçüncü tablo bu nesnelerin çalışma zamanındaki sınırını ölçüyor. Dördünden **üçü**
`isinstance` sınamasına doğrudan verilemiyor; sınama bir sonuç döndürmüyor, istisna
fırlatıyor. Yalnız `str | None` "evet" veriyor, çünkü birleşim yalnız iki tip adını yan
yana koyar ve içeriğe bakmayı gerektirmez.

Neden böyle olduğu bir tasarım sorusu değil, bir maliyet sorusudur. `isinstance(x, list)`
tek bir bakışta yanıtlanır; `isinstance(x, list[int])` bin öğeli bir listede bin sınama
demektir. Sınamanın bedeli kabın boyutuyla büyür ve dil bu bedeli sessizce üstlenmez.

Sonuç, dersin ana savını pekiştiriyor: **açıklama bir sınama değil, bir kayıttır.** Son
satır bunu doğrudan gösteriyor — `toplam(["a", "b"])` çağrısı hata veriyor, ama hatayı
veren açıklama değil **gövdedeki toplama işlemidir**. Açıklama olmasaydı da aynı hata aynı
noktada çıkardı; açıklama olduğu hâlde çağrı engellenmedi.

## Sınanmayan Açıklama Ne İşe Yarar

Sıfır sayısı bir soruyu doğuruyor: sınanmayan bir bildirim neden yazılsın?

Yanıt çıktının ortasındaki satırda duruyor. `yas_yaz.__annotations__` çalışma zamanında
okunabiliyor ve `ad` için `str`, `yas` için `int`, dönüş için `str` veriyor. Açıklama
sınanmıyor ama **kayboluyor da değil**. Bir sözlükte, adı bilinen bir öznitelikte,
program çalışırken erişilebilir hâlde duruyor.

Bu, sonraki üç dersin tamamının dayandığı olgu. Açıklamayı okuyup tip uyuşmazlığı arayan
bir program yazılabilir; kaynağı hiç çalıştırmadan okuyup aynı işi yapan bir program da
yazılabilir; açıklamayı bir şema gibi kullanıp çağrı anında değeri sınayan bir katman da
yazılabilir. Üçünün de hammaddesi bu sözlüktür.

İkinci bir yarar okuyanadır. `def yas_yaz(ad, yas)` imzası çağıranın hangi sırayla neyi
vereceğini söylemez; `def yas_yaz(ad: str, yas: int) -> str` söyler. Bu bir denetim değil,
bir belgedir — ve belge olduğu için de yanlış olabilir. Açıklamayla gövdenin uyuşmasını
sağlayan hiçbir düzenek yoktur; uyuşmayı sağlayan şey, açıklamayı okuyan **başka bir
katmandır**.

Kaydın kendisi de korumalı değildir. `__annotations__` sıradan bir sözlüktür: okunabildiği
gibi değiştirilebilir de. Bir program çalışırken bir işlevin açıklamasını silebilir ya da
başka bir tiple değiştirebilir; işlevin gövdesi bundan etkilenmez ve çağrılar aynı sonucu
vermeye devam eder. Bu, kaydın güvenilmez olduğu anlamına gelmez — kaydın **bağlayıcı
olmadığı** anlamına gelir. Bağlayıcılık ancak kaydı okuyup bir karar veren bir katman
eklendiğinde doğar, ve o katmanı bu kursun geri kalanı kurar.

## Özet

- Tip ipucu bir adın hangi tipte değer taşıyacağını bildiren sözleşmedir; açıklama o
  ipucunun koda yazılmış hâlidir ve işlev imzasında, sınıf gövdesinde, modül düzeyinde ve
  işlev gövdesinde yazılabilir.
- Açıklama üç yerde saklanır ve bir yerde saklanmaz: işlev **3**, sınıf **2**, modül **1**
  öğe verir; işlev gövdesindeki yerel açıklama **0** verir ve geride kayıt bırakmaz.
- Yorumlayıcı açıklamayı saklar, sınamaz; açıklamanın bir tip olması bile gerekmez — dönüş
  açıklaması olarak yazılan bir sayı sözlükte olduğu gibi durur ve çağrı çalışır.
- Beş çağrının **4**'ü ihlallidir — **2** tip ihlali, **2** alan ihlali — ve çalışma
  zamanında hata veren sayısı **0**'dır. Bu ölçümde ihlalleri yakalayan katman yoktur.
- Bileşik açıklamaların dördünden **üçü** `isinstance` sınamasına doğrudan verilemez;
  açıklama bir sınama değil, bir kayıttır ve kayıt bağlayıcı değildir.
- Tipin doğru olması değerin geçerli olduğu anlamına gelmez: boş dizgi bir dizgidir, **-5**
  bir tam sayıdır. Açıklama tipi bildirir, değeri değil.

## Sonraki Adım

`yas_yaz` işlevinin açıklaması iki somut tip adı taşıyor ve bu yüzden yalnız o iki tiple
çalışan bir sözleşme bildiriyor. Oysa çok sayıda işlev tipe kayıtsızdır: bir listenin ilk
öğesini veren işlev listenin neyle dolu olduğunu umursamaz, ama **döndürdüğü şeyin
listenin içindekiyle aynı tipte** olduğunu bilir. Bu bağ somut bir tip adıyla yazılamaz —
yazılırsa ya bağ kaybolur ya da işlev gereksiz yere daraltılır. Sonraki ders aynı işlevin
dört ayrı yazımını yan yana koyuyor ve tek bir sayıyı soruyor: her yazımın açıklaması, bir
çağrının dönüş tipini kaç kez bildirebiliyor?
