---
title: Dekoratörler
source: 'https://academia.sh/tr/kurslar/python-veri-yapilari/dekoratorler'
course: 'Veri Yapıları ve Fonksiyonel Araçlar'
language: tr
updated: '2026-08-17T18:10:26+00:00'
license: 'CC BY-SA 4.0'
---

# Dekoratörler

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.
