---
title: 'Kapsülleme Sözleşmeleri'
source: 'https://academia.sh/tr/kurslar/python-nesne-ve-tipler/kapsulleme-sozlesmeleri'
course: 'Nesneye Dayalı Python ve Tipler'
language: tr
updated: '2026-08-17T18:10:30+00:00'
license: 'CC BY-SA 4.0'
---

# Kapsülleme Sözleşmeleri

Dört erişim biçiminin 3'ü değeri verir ve adın engellediği erişim 0'dır; çift alt çizgi bir kilit değil bir ad değişimidir ve iki sınıfın aynı adı için 1 yerine 2 giriş açar.

Önceki ders iki tasarımı yanıtlayan sınıf üzerinden karşılaştırdı ve `Sarmal`'ın içeride iki
nesne tuttuğunu gösterdi. O iki nesne `Sarmal`'ın **iç düzenidir**: yarın biri kaldırılabilir,
adı değişebilir, ikisi tek nesnede birleşebilir. Ama ölçümde hiçbir şey onlara dışarıdan
dokunulmasını engellemedi — `nesne.imzali` yazımı `nesne.kaynak()` kadar geçerlidir.

Bu ders o sınırın nasıl bildirildiğini ölçer. Bazı diller görünürlüğü dilin kendisiyle
kapatır; Python kapatmaz. Sorulan şey şudur: adın başına konan alt çizgi erişimi gerçekten
**engelliyor** mu, iki alt çizgi ne değiştiriyor, ve engellemiyorsa bu bildirimin ölçülebilir
bir işi var mı?

## Üç Adlandırma, Dört Erişim

Bir sınıf üç ayrı adlandırma biçimi kullanabilir. Alt çizgisiz ad **dışarıya açıktır**: bu
adın adı, tipi ve anlamı sınıfın verdiği sözün parçasıdır. Tek alt çizgiyle başlayan ad
**içeridir**: sınıf onu kullanır, dışarıdan okuyan kendi sorumluluğuna okur, ve sınıf onu
haber vermeden değiştirebilir. İki alt çizgiyle başlayan ad ise dilin **ad değişimi (name
mangling)** kuralını tetikler; adın önüne sınıfın adı eklenir.

Üçü de aynı biçimde atanır ve üçü de aynı sözlüğe yazılır. Ayrıldıkları yer, dışarıdan hangi
yazımla okunabildikleridir.

- **SN27** — Üç ad tek bir `__init__` gövdesinde, birbirinin aynısı olan üç atamayla kurulur;
  değerleri de adlarını yansıtır.
- **SN28** — Dört erişim biçimi denenir: üç ad doğrudan, çift alt çizgili ad ise bir de
  değişmiş yazımıyla. Erişemeyen biçim ünlem önekiyle **istisnanın adını** kaydeder.
- **SN29** — Sınıfın kendi gövdesinden yapılan okuma ayrıca basılır; ölçüm dışarıdan ve
  içeriden erişimi karşılaştırır.
- **SN30** — Örnek sözlüğündeki adlar sıralanmış olarak basılır; **hiçbir kimlik sayısı ya da
  ortama bağlı veri basılmaz.**

```python
"""Ad neyi engelliyor: uc gorunurluk bildirimi ve dort erisim bicimi."""


class Depo:
    """Uc ayri adlandirma bicimi; ucu de ayni sekilde atanir."""

    def __init__(self):
        self.acik = "acik"
        self._ic = "ic"
        self.__kapali = "kapali"

    def kendi_okur(self):
        return (self.acik, self._ic, self.__kapali)


d = Depo()

BICIMLER = (
    ("d.acik", lambda: d.acik),
    ("d._ic", lambda: d._ic),
    ("d.__kapali", lambda: getattr(d, "__kapali")),
    ("d._Depo__kapali", lambda: d._Depo__kapali),
)


def dene(cagri):
    try:
        return repr(cagri())
    except AttributeError as e:
        return f"!{type(e).__name__}"


print(f"{'yazım':<22s} {'dışarıdan erişim':>17s}")
for ad, cagri in BICIMLER:
    print(f"{ad:<22s} {dene(cagri):>17s}")

veren = sum(1 for _, c in BICIMLER if not dene(c).startswith("!"))
print()
print(f"sınıfın kendisi üçünü de okur -> {d.kendi_okur()}")
print(f"örnek sözlüğündeki adlar -> {sorted(d.__dict__)}")
print(f"dört erişim biçiminin {veren}'ü değeri verdi; "
      f"adın engellediği erişim {len(BICIMLER) - veren - 1}")
```

```
yazım                   dışarıdan erişim
d.acik                            'acik'
d._ic                               'ic'
d.__kapali               !AttributeError
d._Depo__kapali                 'kapali'

sınıfın kendisi üçünü de okur -> ('acik', 'ic', 'kapali')
örnek sözlüğündeki adlar -> ['_Depo__kapali', '_ic', 'acik']
dört erişim biçiminin 3'ü değeri verdi; adın engellediği erişim 0
```

Dört biçimin **üçü** değeri veriyor. İlk iki satır beklenen sonucu üretiyor: açık ad da, tek
alt çizgili ad da dışarıdan okunuyor. Tek alt çizginin erişime hiçbir etkisi yoktur; dilin
gözünde `_ic` ile `acik` arasında **hiçbir fark yoktur.** Aradaki tek fark, insanların ve
araçların okuduğu bir bildirimdir.

Üçüncü satır bir engel gibi görünüyor ama değildir; dördüncü satır bunu gösteriyor. Aynı değer
`d._Depo__kapali` yazımıyla sorunsuz okunuyor. Örnek sözlüğüne bakıldığında neden görünür: o
adın sözlükteki karşılığı zaten `_Depo__kapali`'dir. Çift alt çizgi bir kilit değil, **derleme
sırasında yapılan bir ad değişimidir** — sınıf gövdesindeki `self.__kapali` yazımı, adın önüne
sınıfın adı eklenmiş hâline çevrilir. `d.__kapali` yazımının başarısız olmasının nedeni erişimin
kapatılması değil, **öyle bir adın hiç var olmamasıdır.**

Bu yüzden son satırdaki sayı **0**'dır: dört biçimin hiçbirinde erişim engellenmedi, yalnız bir
biçimde aranan ad yanlıştı. Python'da görünürlük dilin uyguladığı bir kural değil, **sınıfın
verdiği bir bildirimdir**; uyup uymamak çağıranın kararıdır ve uymamanın bedeli çalışma zamanında
değil, sınıf değiştiğinde ödenir.

Bildirimin bir alıcısı olduğu için de boş bir jest değildir. Alt çizgili adlar sınıfın açık
yüzeyinin dışında sayılır: bir sınıfın hangi adlarını kullanmanın güvenli olduğunu soran bir
okuyucu, yardım metnini üreten bir araç ve `from ... import *` yazımının hangi adları
getireceğine karar veren kural aynı işareti okur. Bildirimin gücü dilin zorlamasından değil,
**herkesin aynı biçimde okumasından** gelir. Önceki dersin `Sarmal` sınıfı buna örnektir:
içeride tuttuğu iki nesnenin adı `_` ile yazılsaydı, dışarıdan onlara dokunan kodun kırılma
sorumluluğu açıkça devredilmiş olurdu.

## Ad Değişimi Neyi Çözer

Çift alt çizgi erişim engellemiyorsa ne işe yarar? Ölçülebilir bir işi vardır ve tek başına
bir sınıfta görünmez: iki sınıf **aynı adı** kullandığında ortaya çıkar. Kalıtımda taban ve
türev tek bir örnek sözlüğünü paylaşır; ikisi de `self.durum` yazarsa ikinci yazma birincisini
**siler**. Ad değişimi her sınıfın adını öneke koyduğu için bu çakışmayı ortadan kaldırır.

- **SN31** — Taban ve türev aynı iki adı yazar; türevin kurulum gövdesi önce tabanınkini
  çağırır, sonra kendi değerlerini atar.
- **SN32** — Her sınıfın kendi gövdesinden yapılan okuma ayrı ayrı basılır; ölçülen şey aynı
  yazımın iki gövdede **hangi değeri** gördüğüdür.
- **SN33** — Son satır adların örnek sözlüğünde kaç giriş açtığını sayar.

```python
class Taban:
    def __init__(self):
        self._durum = "Taban"
        self.__durum = "Taban"

    def taban_okur(self):
        return {"tek": self._durum, "cift": self.__durum}


class Turev(Taban):
    def __init__(self):
        super().__init__()
        self._durum = "Turev"
        self.__durum = "Turev"

    def turev_okur(self):
        return {"tek": self._durum, "cift": self.__durum}


t = Turev()
print(f"{'yazım':<14s} {'Taban gövdesi okur':>19s} {'Turev gövdesi okur':>19s}")
for anahtar, ad in (("tek", "_durum"), ("cift", "__durum")):
    print(f"{ad:<14s} {t.taban_okur()[anahtar]:>19s} {t.turev_okur()[anahtar]:>19s}")

print()
print(f"örnek sözlüğündeki adlar -> {sorted(t.__dict__)}")
print(f"iki sınıfın tek alt çizgili adı için giriş "
      f"{sum(1 for a in t.__dict__ if a == '_durum')}, "
      f"çift alt çizgili adı için {sum(1 for a in t.__dict__ if a.endswith('__durum'))}")
```

```
yazım           Taban gövdesi okur  Turev gövdesi okur
_durum                       Turev               Turev
__durum                      Taban               Turev

örnek sözlüğündeki adlar -> ['_Taban__durum', '_Turev__durum', '_durum']
iki sınıfın tek alt çizgili adı için giriş 1, çift alt çizgili adı için 2
```

İlk satır çakışmanın kendisidir. `Taban`'ın gövdesi `self._durum` okuduğunda **`Turev`**
değerini görüyor — kendi yazdığı değeri değil. Türevin kurulumu tabanınkinden sonra koştuğu
için aynı sözlük girişinin üzerine yazdı ve tabanın değeri kayboldu. Taban sınıfı bu adı yazan
tek taraf olduğunu sanıyordu; bir alt sınıf farkında olmadan onu bozdu.

İkinci satırda çakışma yok. Her gövde kendi yazdığı değeri okuyor, çünkü iki yazım iki ayrı ada
çevrildi. Alt satırdaki sayılar bunu doğruluyor: tek alt çizgili ad iki sınıf için **1** giriş
açıyor, çift alt çizgili ad **2** giriş açıyor. Sözlükteki adlar da bunu gösteriyor.

Ad değişiminin çözdüğü sorun budur ve tek budur: bir sınıfın iç adının, kim türetirse türetsin
**yalnız kendisine** ait kalması. Bir sınıf başkalarının türetmesi beklenen bir tabansa ve iç
durumunun alt sınıflarca ezilmemesi gerekiyorsa çift alt çizgi ölçülebilir bir güvence verir.
Sıradan bir iç ad için ise tek alt çizgi yeterlidir — çift alt çizgi orada yalnız yazımı
uzatır ve alt sınıfın erişimini gereksizce zorlaştırır. Ad değişiminin **sınıf adına** bağlı
olduğunu da eklemek gerekir: iki ayrı yerde aynı ada sahip iki sınıf varsa önek de aynı çıkar
ve güvence düşer. Verilen söz "kimse erişemez" değil, "aynı adı taşımayan sınıflar
çakışmaz"dır.

## Bildirim Değişmezi Korumaz

Kapsüllemenin amacı adı gizlemek değil, bir **değişmezi** ayakta tutmaktır. Nesneye dayalı
programlamada durumun korunması ve değişmezlerin sürdürülmesi Programlama Paradigmaları
kursunda kurulmuştu; **bu tartışma tekrarlanmaz.** Burada ölçülen şey bir soru: adlandırma
bildirimi değişmezi korumaya yeter mi?

- **SN34** — Ölçülen sınıfın tek değişmezi vardır: tutulan toplam, öğelerin toplamına eşit
  olmalıdır. Tutarlılık her yazmadan sonra sınanır.
- **SN35** — Beş yazma denenir: üçü sınıfın yöntemi üzerinden, ikisi doğrudan iç adlara.
  İkisi de tek alt çizgili adları kullanır ve **hiçbiri engellenmez.**
- **SN36** — "Gövdeden geçti" sütunu yazmanın sınıfın yöntemine uğrayıp uğramadığını, öteki
  sütun o yazmadan sonra değişmezin ayakta olup olmadığını gösterir.
- **SN37** — Yazmalar sırayla ve aynı nesne üzerinde yapılır; bozulan bir değişmez sonraki
  satırlarda da bozuk kalır.

```python
class Sayac:
    """Degismez: _toplam her zaman _ogeler toplamina esittir."""

    def __init__(self):
        self._ogeler = []
        self._toplam = 0

    def ekle(self, n):
        self._ogeler.append(n)
        self._toplam += n

    def tutarli(self):
        return self._toplam == sum(self._ogeler)


s = Sayac()
YAZMALAR = (
    ("s.ekle(2)", lambda: s.ekle(2)),
    ("s.ekle(3)", lambda: s.ekle(3)),
    ("s.ekle(5)", lambda: s.ekle(5)),
    ("s._toplam = 99", lambda: setattr(s, "_toplam", 99)),
    ("s._ogeler.append(7)", lambda: s._ogeler.append(7)),
)

print(f"{'yazma':<21s} {'gövdeden geçti':>15s} {'değişmez korundu':>17s}")
gecen = korunan = 0
for ad, islev in YAZMALAR:
    govdeden = ad.startswith("s.ekle")
    islev()
    korundu = s.tutarli()
    gecen += govdeden
    korunan += korundu
    print(f"{ad:<21s} {str(govdeden):>15s} {str(korundu):>17s}")

print()
print(f"yazma {len(YAZMALAR)}, gövdeden geçen {gecen}, "
      f"adın engellediği 0, değişmezin korunduğu {korunan}")
```

```
yazma                  gövdeden geçti  değişmez korundu
s.ekle(2)                        True              True
s.ekle(3)                        True              True
s.ekle(5)                        True              True
s._toplam = 99                  False             False
s._ogeler.append(7)             False             False

yazma 5, gövdeden geçen 3, adın engellediği 0, değişmezin korunduğu 3
```

Beş yazmanın **üçü** sınıfın gövdesinden geçti ve üçünde de değişmez ayakta kaldı. İkisi
geçmedi ve ikisi de değişmezi bozdu. Adın engellediği yazma sayısı yine **0**'dır: iki ihlal
de tek alt çizgili adlara yapıldı ve hiçbiri istisna üretmedi.

Son satır bir sınırı bozmayı da gösteriyor. `s._ogeler.append(7)` bir **atama** bile değil;
okunan bir nesne üzerinde yapılan bir değişikliktir. Bir adı okutmak, o adın gösterdiği
değiştirilebilir nesneyi de teslim etmektir. Kapsüllemenin bu tarafı adlandırmayla hiç
çözülemez; ancak dışarıya kopya vermekle ya da değiştirilemez bir kap vermekle çözülür, ve
ikisi de gövdede alınmış bir karardır.

Ölçümün sıralı yapılması bir noktayı daha gösteriyor. Dördüncü satırda bozulan değişmez
beşinci satırda da bozuk; nesne kendini onarmıyor ve kimse durumu sorgulamıyor. Bozulmanın
ortaya çıkacağı yer, ihlalin yapıldığı satır değil, toplamın **okunduğu** herhangi bir satır
olacaktır. İhlal ile belirtinin arasının açılması, denetimsiz yazmanın asıl bedelidir.

"Kim" sorusunun bu dersteki yanıtı tektir: **değişmezi koruyan tek yer `ekle` gövdesidir.**
Ad ne yapıyorsa yapsın, o gövdeye uğramayan her yazma denetimsizdir. Adlandırma bildirimi
sözleşmenin **hangi yazımların desteklendiğini** söyler; sözleşmeyi **uygulayan** taraf değildir.
Uygulayan tarafı yazmak ayrı bir iştir ve erişimin kendisini araya bir gövde koyarak yakalamayı
gerektirir.

## Gerçekten Engelleyen Tek Bildirim

Adlandırma hiçbir şeyi engellemiyorsa, engelleyen bir bildirim var mıdır? Bir tane vardır ve
görünürlüğü değil, **öznitelik kümesini** bildirir. Bir sınıf gövdesinde `__slots__` adıyla
yazılan bir dizi, o sınıfın örneklerinin hangi adları taşıyabileceğini sabitler; bildirilmemiş
bir ada yazma çalışma zamanında düşer ve örnekler ayrı bir sözlük taşımaz.

- **SN38** — İki sınıf aynı kurulum gövdesini paylaşır; tek fark birinin öznitelik kümesini
  bildirmesi, ötekinin bildirmemesidir.
- **SN39** — İki yazma denenir: bildirilmiş bir ada ve bir harf farkla yanlış yazılmış ada.
  İkincisi bilerek yanlıştır.
- **SN40** — "Örnek sözlüğü" sütunu örneğin ayrı bir öznitelik sözlüğü taşıyıp taşımadığını
  bildirir; **içeriği ve büyüklüğü basılmaz.**
- **SN41** — "Engellenen" sütunu iki yazmadan kaçının istisnayla düştüğünü sayar.

```python
class Serbest:
    """Oznitelik kumesini bildirmez: her ad yazilabilir."""

    def __init__(self, ad):
        self.ad = ad


class Kisitli:
    """Oznitelik kumesini bildirir: yalniz bildirilen adlar yazilabilir."""

    __slots__ = ("ad",)

    def __init__(self, ad):
        self.ad = ad


def yaz(nesne, alan):
    try:
        setattr(nesne, alan, "yeni")
    except AttributeError as e:
        return f"!{type(e).__name__}"
    return "geçti"


print(f"{'sınıf':<10s} {'ad yazımı':>16s} {'adi yazımı':>16s} "
      f"{'örnek sözlüğü':>14s} {'engellenen':>11s}")
for s in (Serbest, Kisitli):
    n = s("k")
    sonuc = [yaz(n, "ad"), yaz(n, "adi")]
    print(f"{s.__name__:<10s} {sonuc[0]:>16s} {sonuc[1]:>16s} "
          f"{('var' if hasattr(n, '__dict__') else 'yok'):>14s} "
          f"{sum(1 for c in sonuc if c.startswith('!')):>11d}")
```

```
sınıf             ad yazımı       adi yazımı  örnek sözlüğü  engellenen
Serbest               geçti            geçti            var           0
Kisitli               geçti  !AttributeError            yok           1
```

İlk satır bildirimsiz durumun sessizliğini gösteriyor: yanlış yazılmış ad da **geçiyor** ve
nesneye yeni bir öznitelik ekliyor. Program hata vermez; yalnız `ad` eski değerinde kalır ve
kimse bunu fark etmez. Alt satırda aynı yazma **düşüyor**; engellenen yazma sayısı **1**'e
çıkıyor.

Bu, dersin tek gerçek engelidir ve dikkat çekici biçimde bir **görünürlük** engeli değildir.
`__slots__` bildirimi hangi adların yazılabileceğini söyler; hangi adların dışarıdan
okunabileceğini yine söylemez. Bildirilmiş bir ad, alt çizgili de olsa dışarıdan okunmaya
açıktır. Yani Python'da uygulanan tek kapsülleme kuralı, kümenin **kapalılığıdır**, kümenin
**gizliliği** değildir.

Örnek sözlüğünün yokluğu bunun düzeneğini de açıklıyor. Bildirim yapıldığında örnek adları
serbest bir sözlükte tutmaz; her ad için sınıf üzerinde sabit bir yuva kurulur ve o yuvalar
dışında yazılacak yer kalmaz. Engelin kaynağı bir yasak değil, **yerin yokluğudur.**

## Özet

- Python'da görünürlük dilin uyguladığı bir kural değil, adlandırmayla yapılan bir bildirimdir;
  dört erişim biçiminin **3**'ü değeri verir ve adın engellediği erişim **0**'dır.
- Tek alt çizgili adın erişime hiçbir etkisi yoktur; sınıfın iç düzenine ait olduğunu ve haber
  verilmeden değişebileceğini bildirir.
- Çift alt çizgi bir kilit değil, sınıf adını öneke koyan bir **ad değişimidir**; `d.__kapali`
  yazımının düşmesinin nedeni erişimin kapatılması değil, o adın hiç var olmamasıdır.
- Ad değişiminin ölçülebilir işi çakışmayı önlemektir: taban ile türevin aynı adı için tek alt
  çizgide **1** giriş açılır ve tabanın değeri kaybolur, çift alt çizgide **2** giriş açılır ve
  her gövde kendi değerini okur.
- Değişmezi koruyan tek yer yöntem gövdesidir; beş yazmanın **3**'ü gövdeden geçti ve değişmez
  **3**'ünde ayakta kaldı, geçmeyen **2** yazma onu bozdu.
- Uygulanan tek bildirim öznitelik kümesini kapatan `__slots__`'tur: bildirimsiz sınıfta yanlış
  yazılmış ad sessizce **geçer**, bildirimli sınıfta **düşer**. Kapatılan şey kümedir,
  görünürlük değildir.

## Sonraki Adım

Buraya kadar yazdığımız sınıflar kendi yöntemleriyle çağrıldı: `ekle`, `bicimle`, `kaynak`.
Oysa önceki iki kursta ölçülen protokollerin çoğu yöntem adıyla değil, **sözdizimle**
çağrılıyordu — `len(n)`, `n == m`, `with n`, `for x in n`. O sözdizimleri dilin verdiği
nesneler karşılıyordu ve karşılayan taraf hep hazırdı. Sonraki ders karşılayan tarafı bizim
yazdığımız durumu ölçer: kendi sınıfımız hiçbir özel yöntem yazmadan bu sözdizimlerin kaçına
katılabiliyor, katılamadıklarında ne oluyor, ve yazdığımız her yöntem hangi sözdizimi açıyor?
