---
title: 'Genellikler ve Tip Değişkenleri'
source: 'https://academia.sh/tr/kurslar/python-nesne-ve-tipler/genellikler-ve-tip-degiskenleri'
course: 'Nesneye Dayalı Python ve Tipler'
language: tr
updated: '2026-08-17T18:10:30+00:00'
license: 'CC BY-SA 4.0'
---

# Genellikler ve Tip Değişkenleri

Aynı işlevin dört yazımı dört çağrı yerinde ayrı sonuç verir: açıklamasız yazım iki ihlali kaçırır, gevşek yazım ikisini bulur ama iki yanlış alarm üretir, genellikli yazım ikisini de bulur ve hiç yanlış alarm vermez.

Önceki ders açıklamanın saklandığını ve sınanmadığını ölçtü; beş çağrının dördü ihlalliydi
ve çalışma zamanında hata veren sıfırdı. Ölçülen işlevin açıklaması iki somut tip adı
taşıyordu: `ad: str`, `yas: int`. Böyle bir imza yalnız o iki tiple çalışan bir sözleşme
bildirir ve bildirdiği şey açıktır.

Çok sayıda işlev ise tipe kayıtsızdır. Bir listenin ilk öğesini veren işlev listenin neyle
dolu olduğunu umursamaz — ama umursamadığı hâlde bir şey bilir: **döndürdüğü şey, listenin
içindekiyle aynı tiptedir.** Bu bağ somut bir tip adıyla yazılamaz. Bu dersin sorusu, o
bağın yazılıp yazılamayacağı ve yazıldığında ne kazandırdığıdır. Ölçü yine kursun ekseninde:
**hangi yazım hangi ihlali bildirebiliyor?**

## Somut Tip Adının İki Ucu

`ilk` işlevini yazmanın iki kolay yolu var ve ikisi de bir şey kaybettiriyor.

Birinci yol açıklamayı **gevşetmektir**: `def ilk(dizi: list) -> object`. Bu imza her
listeyi kabul eder, doğrudur ve hiçbir çağrıyı gereksiz yere reddetmez. Kaybettiği şey
bağdır — dönüş için "bir nesne" demek, çağıranın elindeki değer hakkında hiçbir şey
söylememektir.

İkinci yol açıklamayı **somutlaştırmaktır**: `def ilk(dizi: list[int]) -> int`. Bu imza
dönüşü tam olarak bildirir. Kaybettiği şey işlevin kendisidir — gövde her listeyle
çalıştığı hâlde imza yalnız tam sayı listelerini kabul eder, ve dizgi listesiyle yapılan
geçerli bir çağrı da ihlal sayılır.

İkisinin ortası yok gibi görünüyor: ya bağı bırakacaksınız ya işlevi daraltacaksınız.
Genellik tam olarak bu ikilemi ortadan kaldıran yazımdır.

## Tip Değişkeni

**Tip değişkeni (type variable)**, bir tip adının yerini tutan addır. `T = TypeVar("T")`
satırı böyle bir ad üretir; `T` kendisi bir tip değildir, imzada bir tipin **duracağı yeri**
gösterir.

```python
from typing import TypeVar

T = TypeVar("T")


def ilk(dizi: list[T]) -> T:
    return dizi[0]
```

Bu imza iki şey söylüyor. Birincisi: argüman herhangi bir tiple dolu bir listedir — kabul
edilen küme daralmadı. İkincisi: dönüş, o listenin içindekiyle **aynı** tiptedir. Aynı adın
iki yerde geçmesi bağı kuran şeydir; `T` bir yerde ne ise öbüründe de odur.

Aynı adı taşıyan bir imzaya sahip yapıya **genellik (generic)** denir. Genellik bir işlevde
olabildiği gibi bir sınıfta da olabilir: `Generic[T]`'ten türeyen bir sınıf, taşıdığı
değerin tipini imzalarına yayabilir.

Tip değişkeni tamamen serbest bırakılmak zorunda değil. **Bağlı** bir tip değişkeni bir üst
sınır bildirir — `TypeVar("Yazi", bound=str)` yalnız dizgi ve alt sınıflarını kabul eder.
**Sınırlı** bir tip değişkeni ise sonlu bir seçenek listesi bildirir:
`TypeVar("Sayisal", int, float)`. İkisinin de sınırı çalışma zamanında okunabilir; ölçüm bu
sınırların okunmakla sınanmak arasındaki farkı da gösteriyor.

## Ölçümün Kurgusu

Ölçüm dört yazımı dört çağrı yerinde karşılaştırıyor. Her çağrı yeri iki şeyden oluşuyor:
`ilk` çağrısı ve onun sonucuna uygulanan bir işlem. İşlem sonuçtan bir tip **bekler** —
`+ 1` bir tam sayı, `.upper()` bir dizgi. Çağrı yerinin ihlalli olması, listenin öğe
tipiyle işlemin beklediği tipin uyuşmamasıdır.

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

- **TP9** — Kâhin kurgunun kendisidir: dört çağrı yerinden hangisinin ihlalli olduğunu
  listenin öğe tipi ile işlemin gerektirdiği tip belirler; ikisini de dersin yazarı yazdı.
- **TP10** — Dört çağrı yerinin **ikisi** ihlallidir. Küme küçük tutulmuştur, çünkü ölçülen
  şey ihlal oranı değil, **her yazımın bildirebildiği ihlal kümesidir**.
- **TP11** — Çıkarım adımı ders içinde modellenmiştir: bir dönüş açıklamasından çağrı
  yerinin dönüş tipini üretir ve tip değişkenini argümandaki öğe tipiyle değiştirir. Tam
  denetleyici bu derste kurulmaz.
- **TP12** — "Bildirilen", yazımın o çağrı yerinde bir sorun bildirmesidir; iki nedenden
  biriyle olur: argüman açıklamaya uymaz, ya da çıkarılan dönüş tipi işlemin beklediğinden
  başkadır.
- **TP13** — "Doğru", bildirimin gerçekten ihlalli bir çağrı yerine düşmesidir.
  "Kaçırılan", ihlalin bildirilmemesidir. "Yanlış alarm", uygun bir çağrı yerinde bildirim
  yapılmasıdır.
- **TP14** — `object` dönüşü hiçbir işlemin beklediği tiple aynı sayılmaz; gevşek yazımın
  dört çağrı yerinde de bildirim yapması bu kuralın sonucudur.
- **TP15** — Çalışma zamanı sütunu dört çağrı yerinin **gerçekten çalıştırılmasıyla**
  ölçülür ve yalnız tip kaynaklı istisnalar sayılır.
- **TP16** — Genel sınıf ölçümünde kimlik `is` ile gösterilir; hiçbir kimlik sayısı
  basılmaz.
- **TP17** — Bağlı ve sınırlı tip değişkenlerinin ölçüsü, sınırın okunabilir olması ve
  sınırı bozan bir çağrının çalışıp çalışmamasıdır. İki argümanlı ölçümde birleştirme adımı
  da ders içinde modellenmiştir: tip değişkeni ilk eşleşmede bağlanır, sonraki
  eşleşmelerde bağın tutarlılığı sınanır.

## Ölçüm

```python
"""Genellikler: hangi yazim hangi ihlali bildirebiliyor."""
from typing import Generic, TypeVar

T = TypeVar("T")


def ilk(dizi):
    return dizi[0]


YAZIMLAR = (("açıklamasız", None, None),
            ("list -> object", list, object),
            ("list[int] -> int", list[int], int),
            ("list[T] -> T", list[T], T))

# cagri yeri: kaynak listenin oge tipi, sonraki islemin gerektirdigi tip
YERLER = (("ilk([1, 2]) + 1", int, int),
          ("ilk(['a', 'b']) + 1", str, int),
          ("ilk(['a', 'b']).upper()", str, str),
          ("ilk([1, 2]).upper()", int, str))


def kabul(param, oge):
    """Aciklama bu cagri yerinin argumanini kabul ediyor mu."""
    ic = getattr(param, "__args__", (None,))[0]
    return param is None or param is list or ic is T or ic is oge


def cikarim(donus, oge):
    """Donus aciklamasindan cagri yerinin donus tipi cikarilabiliyor mu."""
    if donus is None:
        return None
    return oge if donus is T else donus


print(f"{'yazım':<18s} {'çıkarılan':>10s} {'bildirilen':>11s} {'doğru':>6s}"
      f" {'kaçırılan':>10s} {'yanlış alarm':>13s}")
for ad, param, donus in YAZIMLAR:
    cikan = bildirilen = dogru = kacirilan = yanlis = 0
    for yer, oge, gereken in YERLER:
        gercek_ihlal = oge is not gereken
        c = cikarim(donus, oge)
        cikan += c is not None
        bildirim = (not kabul(param, oge)) or (c is not None and c is not gereken)
        bildirilen += bildirim
        dogru += bildirim and gercek_ihlal
        kacirilan += gercek_ihlal and not bildirim
        yanlis += bildirim and not gercek_ihlal
    print(f"  {ad:<16s} {f'{cikan}/4':>10s} {bildirilen:11d} {dogru:6d}"
          f" {kacirilan:10d} {yanlis:13d}")

GERCEK = (lambda: ilk([1, 2]) + 1,
          lambda: ilk(["a", "b"]) + 1,
          lambda: ilk(["a", "b"]).upper(),
          lambda: ilk([1, 2]).upper())
calisan = 0
for f in GERCEK:
    try:
        f()
    except (TypeError, AttributeError):
        calisan += 1
print(f"çağrı yeri {len(YERLER)}, gerçek ihlal "
      f"{sum(o is not g for _, o, g in YERLER)}, "
      f"çalışma zamanında hata veren {calisan}")


def esle(a, b):
    return a if a == b else b


CIFTLER = ((int, int), (int, str), (str, str))
IMZALAR = (("a: object, b: object", (object, object)),
           ("a: T, b: object", (T, object)),
           ("a: T, b: T", (T, T)))


def birlestir(imza, tipler):
    """Tip degiskenini ilk eslesmede baglar, sonra tutarliligi sinar."""
    bag = {}
    for aciklama, tip in zip(imza, tipler):
        if aciklama is T:
            if T in bag and bag[T] is not tip:
                return False
            bag[T] = tip
        elif aciklama is not object and aciklama is not tip:
            return False
    return True


print()
print(f"{'iki argümanlı imza':<24s} {'bildirilen':>11s}")
for ad, imza in IMZALAR:
    print(f"  {ad:<22s} "
          f"{sum(not birlestir(imza, c) for c in CIFTLER):11d}")
print(f"çift {len(CIFTLER)}, tipleri ayrı olan "
      f"{sum(x is not y for x, y in CIFTLER)}, "
      f"esle(1, 'a') -> {esle(1, 'a')!r} (çalışma zamanı hata vermedi)")


class Kutu(Generic[T]):
    def __init__(self, deger: T):
        self.deger = deger

    def al(self) -> T:
        return self.deger


k = Kutu[int]("dizgi")
print()
print(f"Kutu[int]('dizgi').al() -> {k.al()!r}")
print(f"type(k) is Kutu -> {type(k) is Kutu}; "
      f"Kutu[int] is Kutu[str] -> {Kutu[int] is Kutu[str]}")
print(f"isinstance(k, Kutu) -> {isinstance(k, Kutu)}")
try:
    isinstance(k, Kutu[int])
    print("isinstance(k, Kutu[int]) -> sınandı")
except TypeError:
    print("isinstance(k, Kutu[int]) -> sınanamaz")
print(f"al açıklaması: {Kutu.al.__annotations__}")

Yazi = TypeVar("Yazi", bound=str)
Sayisal = TypeVar("Sayisal", int, float)
print()
print(f"bağlı tip değişkeni sınırı: {Yazi.__bound__}")
print(f"sınırlı tip değişkeni seçenekleri: {Sayisal.__constraints__}")


def buyut(d: Yazi) -> Yazi:
    return d


print(f"buyut(7) -> {buyut(7)!r} (bağ çalışma zamanında sınanmadı)")
```

```
yazım               çıkarılan  bildirilen  doğru  kaçırılan  yanlış alarm
  açıklamasız             0/4           0      0          2             0
  list -> object          4/4           4      2          0             2
  list[int] -> int        4/4           3      2          0             1
  list[T] -> T            4/4           2      2          0             0
çağrı yeri 4, gerçek ihlal 2, çalışma zamanında hata veren 2

iki argümanlı imza        bildirilen
  a: object, b: object             0
  a: T, b: object                  0
  a: T, b: T                       1
çift 3, tipleri ayrı olan 1, esle(1, 'a') -> 'a' (çalışma zamanı hata vermedi)

Kutu[int]('dizgi').al() -> 'dizgi'
type(k) is Kutu -> True; Kutu[int] is Kutu[str] -> False
isinstance(k, Kutu) -> True
isinstance(k, Kutu[int]) -> sınanamaz
al açıklaması: {'return': ~T}

bağlı tip değişkeni sınırı: <class 'str'>
sınırlı tip değişkeni seçenekleri: (<class 'int'>, <class 'float'>)
buyut(7) -> 7 (bağ çalışma zamanında sınanmadı)
```

## Dört Yazım, Dört Sonuç

Üst tablonun son üç sütunu birbirinden bağımsız üç kusuru sayıyor: kaçırılan ihlal, yanlış
alarm ve çıkarılamayan dönüş tipi. Dört yazımdan yalnız biri üçünü birden sıfırlıyor.

**Açıklamasız yazım** hiçbir dönüş tipi çıkarmıyor: **0/4**. Bildirim sayısı **0**,
kaçırılan **2**. Yanlış alarm da **0** — hiçbir şey söylemeyen bir imza yanılamaz da. Bu
satır bir ölçüt: aşağıdaki üç yazımın kazandırdığı şey bu satırdan uzaklaşmalarıdır.

**Gevşek yazım** dört çağrı yerinin dördünde de bir dönüş tipi çıkarıyor: **4/4**. Ama
çıkardığı tip her seferinde `object` ve hiçbir işlem `object` beklemiyor. Bildirim **4**,
doğru **2**, yanlış alarm **2**. Her çağrı yerinde bildirim yapan bir yazım, hiçbir çağrı
yerini ötekinden ayırmıyor demektir. Kaçırılan **0**'a indi, karşılığında yanlış alarm
**0**'dan **2**'ye çıktı.

**Aşırı somut yazım** üç bildirim yapıyor: **2** doğru, **1** yanlış alarm. Yanlış alarm
dizgi listesiyle yapılan geçerli çağrıdan geliyor — imza o çağrıyı argüman düzeyinde
reddediyor, oysa gövde onunla sorunsuz çalışır. Doğru bildirimlerden biri de aynı yerden
geliyor, yani doğru sonucu yanlış gerekçeyle veriyor: ihlal dönüş tarafındayken imza
argümanı suçluyor.

**Genellikli yazım** dört çağrı yerinin dördünde dönüş tipini çıkarıyor, **2** bildirim
yapıyor, ikisi de doğru, kaçırılan **0**, yanlış alarm **0**. Kaçırılanı gevşek yazım gibi
sıfırlıyor, yanlış alarmı açıklamasız yazım gibi sıfırlıyor. Tek satırda ikisi birden.

Kazancın kaynağı tek bir yerde: **bağ**. Öbür üç yazım argümanla dönüş arasında bir ilişki
kurmuyor, her birine ayrı ayrı bir tip yazıyor ya da hiç yazmıyor. Genellikli yazım tek bir
adı iki yerde kullanarak ilişkiyi bildiriyor ve çıkarım o ilişkiden çağrı yerine özel bir
yanıt üretebiliyor.

## Aynı Adın İki Argümanda Kullanılması

Bağın gücü dönüşle sınırlı değil. İkinci tablo iki argümanlı bir işlevi ölçüyor: iki değeri
karşılaştırıp birini döndüren `esle`. Sözleşme "iki argüman **aynı** tipte olmalı"
diyecekse, bunu yazmanın tek yolu aynı tip değişkenini iki parametrede kullanmaktır.

Ölçüm bunu bir **birleştirme** adımıyla modelliyor: tip değişkeni ilk eşleşmede bir tipe
bağlanır, sonraki parametrede aynı değişken görülürse bağın tutup tutmadığına bakılır.

Üç imza, üç çift. `object` ile yazılmış imza **0** bildirim yapıyor — tipleri ayrı olan
çift var ama imza onları ayırt edecek hiçbir şey söylemiyor. `a: T, b: object` de **0**
veriyor: tip değişkeni tek yerde geçtiğinde bağ kurulmaz, çünkü bağ **iki uç ister**.
`a: T, b: T` ise **1** bildirim yapıyor ve bu, kümedeki tek gerçek uyuşmazlıktır.

Son satır kim yakaladı sorusunu yine yanıtlıyor: `esle(1, "a")` çağrısı hata vermiyor, bir
değer döndürüyor. **Çalışma zamanı bu uyuşmazlığı görmüyor**; gören tek şey imzadaki tekrar
eden addır.

## Bu Ölçümde Kim Yakaladı

Bildirimleri yapan katman **açıklamayı okuyan taraftır** ve bu derste onun yalnız çıkarım
adımı modellendi; tam denetleyici bir sonraki dersin konusu.

Çalışma zamanı satırı ayrı bir sonuç veriyor: dört çağrı yerinde **2** hata çıkıyor, yani
gerçek ihlallerin ikisi de yakalanıyor. Bu, önceki dersin **0** sonucuyla çelişmiyor — orada gövde her tipi
kabul eden bir biçimlemeydi, burada gövdenin dışındaki işlem tipi gerçekten kullanıyor.
Fark üç noktada duruyor. Çalışma zamanı hatayı **çağrıdan sonra**, `ilk` çoktan döndükten
sonra veriyor. Yalnız **çalıştırılan** satırlarda veriyor; dört çağrı yerinden biri bir
koşulun içinde kalsaydı ve o koşul sağlanmasaydı, o ihlal görünmezdi. Ve hatayı veren şey
açıklama değil, işlemin kendisi — açıklama silinseydi aynı iki hata aynı yerlerde çıkardı.

Genellikli yazımın **2** bildirimi ise hiçbir şey çalıştırmadan, çağrı yerine bakılarak
üretiliyor. İki katmanın yakaladığı ihlaller burada aynı; **yakalama anları** ayrı.

## Genel Sınıf Çalışma Zamanında Ne Olur

Alt bloklar genelliğin çalışma zamanındaki karşılığını ölçüyor ve sonuç önceki dersin
savını doğruluyor.

`Kutu[int]("dizgi")` bir tip ihlalidir: kutunun tam sayı taşıması bildirilmiş, içine dizgi
konmuştur. Çağrı çalışıyor, `al()` dizgiyi geri veriyor, hata yok. `type(k) is Kutu`
**doğru** — üretilen nesnenin sınıfı `Kutu`'dur; köşeli parantezdeki tip nesnenin sınıfına
işlenmez. `Kutu[int] is Kutu[str]` **yanlış**: ikisi ayrı nesnelerdir, ama ikisi de aynı
sınıfın örneklerini üretir.

`isinstance(k, Kutu)` **doğru** veriyor; `isinstance(k, Kutu[int])` ise sınanamıyor.
Önceki dersteki `list[int]` sonucunun aynısı: parametreli bir açıklama çalışma zamanı
sınamasına doğrudan verilemez. `al` yönteminin açıklaması da tip değişkeninin kendisini
taşıyor — çözülmüş bir tip değil, çözülecek yerin adını.

Son iki satır bağlı ve sınırlı tip değişkenlerini ölçüyor. Sınırlar okunabiliyor: bağlı
değişkenin üst sınırı `str`, sınırlı değişkenin seçenekleri `int` ve `float`. Ama sınırı
bozan `buyut(7)` çağrısı çalışıyor ve **7** döndürüyor. **Bağ da açıklamadır:** kaydedilir,
okunur, sınanmaz.

## Özet

- Tip değişkeni bir tip adının yerini tutan addır; aynı adın imzada iki kez geçmesi argüman
  ile dönüş arasında bir **bağ** kurar ve genellik bu bağı taşıyan yazımdır.
- Dört yazım dört çağrı yerinde ayrışıyor: açıklamasız yazım **0** bildirim ve **2**
  kaçırılan, gevşek yazım **4** bildirim ve **2** yanlış alarm, aşırı somut yazım **3**
  bildirim ve **1** yanlış alarm veriyor.
- Genellikli yazım **2** bildirim yapıyor, ikisi de doğru; kaçırılan ve yanlış alarm
  **0**'dır. Kazancın kaynağı bağdır, tip adlarının somutluğu değil.
- Bağ iki uç ister: aynı tip değişkeni iki parametrede geçtiğinde uyuşmazlık bildirilir
  (**1**), tek parametrede geçtiğinde ya da `object` yazıldığında bildirilmez (**0**).
- Bağlı tip değişkeni bir üst sınır, sınırlı tip değişkeni sonlu bir seçenek kümesi
  bildirir; ikisinin de sınırı çalışma zamanında okunabilir ama sınanmaz.
- Genel bir sınıfın köşeli parantezdeki tipi örneğin sınıfına işlenmez: `type(k) is Kutu`
  doğrudur, `Kutu[int]` ile yapılan bir örnek sınaması ise yapılamaz.

## Sonraki Adım

Bu derste açıklamayı okuyan tarafın yalnız bir adımı modellendi: bir çağrı yerinin dönüş
tipini çıkarmak. Gerçek bir statik denetleyici bundan fazlasını yapar — bütün çağrıları
tarar, her birinin argümanlarını imzayla karşılaştırır ve uyuşmayanları çalıştırmadan
bildirir. Sonraki ders o denetleyiciyi ders içinde modelliyor ve önceki dersin beş
çağrısına uyguluyor. Sorulan sayı şudur: dört ihlalin kaçını bulur, kaçını bulamaz — ve
bulamadıklarını neden bulamaz?
