Kapsülleme, property ve geçerli durum

Bu hafta nesnelerin anlamsız değerlere düşmesini önlemeyi öğreneceğiz. Üç kavram kullanacağız: değişmez koşul (invariant), kapsülleme (encapsulation) ve property. Üçünü de aşağıda birer örnekle tanımlayacağız.

Bu bölümün kapsamı

NoteSınavda sorulur
  • Bir sınıf için değişmez koşul (invariant) yazma
  • Geçersiz başlangıç durumunu __init__ içinde engelleme
  • Kapsüllemenin amacı: erişimi yasaklamak değil, niteliklerin yalnız sınıfın metotlarıyla değişmesi
  • Tek alt çizgi geleneği ve Python’da dışarıdan erişimi engelleyen bir private belirtecinin olmaması
  • property ve setter; üç adın aynı olması
  • Doğrulamayı tek yerde tutmak için __init__ içinde setter’ı kullanmak
  • Önce ön koşulları denetleyip sonra niteliği değiştirmek
  • Çift alt çizgi ad dönüştürmesinin (name mangling) nasıl çalıştığı
  • __set_name__, descriptor protokolü
  • dataclass ile doğrulama
  • __getattr__ / __setattr__ ile erişim yakalama

Bu başlıklar konunun devamıdır; ileride karşınıza çıkar ama bu derste ezberlemeniz beklenmiyor.

Nesne geçersiz bir duruma düşebilir mi?

Geçen hafta yazdığımız BankAccount sınıfında account.balance = -500 satırını hiçbir şey durdurmaz. Bakiyesi eksiye düşen bir hesaba ya da fiyatı sıfırın altında bir ürüne izin veren program çalışır, ama doğru çalışmaz. Bu bölümde bu tür kuralları sınıfın koduna yazacağız. Kurala uymayan bir değer geldiğinde program hata verecek.

Değişmez koşul (invariant) nedir?

Değişmez koşul (invariant), nesne kullanılırken her zaman doğru olması gereken kuraldır. Niteliklerin değeri değişir, kural değişmez: bir hesabın bakiyesi 100 de olabilir 20 de, ama eksi olamaz.

Bir banka hesabı için:

balance >= 0

Bir ürün için:

price > 0 ve stock >= 0

Bir ders kaydı için:

0 <= score <= 100

Bu kurallar nesnenin ne olduğunu da söyler: puanı 130 olan bir ders kaydı artık anlamlı bir kayıt değildir.

Geçersiz başlangıcı engellemek

__init__ kuralı daha nesne oluşturulurken denetler. Negatif bakiyeli bir hesap hiç oluşmaz; nesne ilk andan itibaren geçerlidir.

Bakiyeyi yalnız metotlar değiştirsin

Bakiyeyi değiştiren iki metot var, deposit ve withdraw. İkisi de bakiyeye dokunmadan önce tutarı denetliyor. Dışarıdaki kod bakiyeyi account.balance ile okur. @property ile işaretlenen balance metodu parantezsiz çağrılır ve _balance değerini döndürür; property ayrıntısını aşağıda göreceğiz.

_balance adının başındaki tek alt çizgi, Python programcıları arasındaki bir adlandırma geleneğidir. Anlamı şudur: “Bu nitelik sınıfın iç ayrıntısıdır, dışarıdan dokunmayın.” Python bu niteliğe erişimi teknik olarak engellemez.

Nesne durumu yalnızca kurallı davranışlarla değiştiğinde invariantın korunduğunu gösteren diyagram.

Kapsülleme ve invariant
ImportantKapsüllemede amaç kuralı korumaktır

Bir nesnenin dışarıdaki koda açtığı metot ve niteliklere o nesnenin arayüzü denir. Kapsüllemede amaç, niteliklerin yalnızca arayüzdeki kurallı metotlarla değişmesidir. Bazı dillerde bir nitelik private diye işaretlenir ve ona sınıf dışından erişim tamamen engellenir. Python’da kapsülleme çoğu zaman böyle bir engelle değil, sorumluluk ve arayüz disipliniyle korunur: programcılar alt çizgili adlara dokunmaz, niteliği sınıfın metotlarıyla değiştirir.

Tek alt çizgi ve çift alt çizgi

Python’da _balance yalnızca bir adlandırma geleneğidir (convention). Okuyana “bu ad sınıfın ya da modülün iç ayrıntısıdır” der. __balance gibi iki alt çizgiyle başlayan bir ad ise sınıf gövdesinde Python’ın o adı değiştirmesine yol açar. Buna ad dönüştürme (name mangling) denir:

class Account:
    def __init__(self):
        self.__balance = 100

Python bu adı kabaca _Account__balance biçimine dönüştürür. Bunun amacı niteliği gizlemek ya da korumak değildir. Bir sınıftan yeni bir sınıf türetildiğinde (buna kalıtım denir, 10. bölümde göreceğiz) iki sınıftaki aynı adlı niteliklerin yanlışlıkla çakışmasını azaltır.

Warning__x gerçek anlamda private değildir

Dönüştürülmüş ada account._Account__balance yazarak yine erişebilirsiniz. Yani çift alt çizgili bir niteliğe “kimse erişemez” demek yanlıştır. Bu derste sınıfın iç verisi için çoğunlukla tek alt çizgi (_balance) kullanacağız. __x biçiminde bir ad gördüğünüzde ad dönüştürmenin çalıştığını bilmeniz yeterlidir.

property ne zaman anlamlı?

Bazen bir niteliğin account.balance diye okunmasını, ama yalnız kurala uyan değerlerle değişmesini isteriz. Python’da bunun aracı propertydir: dışarıdan nitelik gibi görünen, ama okununca ya da atanınca bir metot çalıştıran bir ad.

property ve setter'ın bir niteliği okumak ve değiştirmek için kontrollü bir arayüz oluşturduğunu gösteren diyagram.

property erişim kapısı
@property
def balance(self):
    return self._balance

@property ile işaretlenen metot, değer okunurken çalışır; buna getter denir. account.balance yazınca bu metot çalışır ve _balance değerini döndürür. Değer atanırken çalışan metoda ise setter denir. Burada setter tanımlamadığımız için account.balance = -100 gibi bir atama yapılamaz.

Değerin değişebilmesi, ama yalnız kurala uyarak değişmesi gerekiyorsa setter yazılır:

Setter söz diziminde üç ad aynı olmalıdır

Yukarıdaki örnekte özellikle şuna dikkat edin:

@property
def price(self):
    ...

@price.setter
def price(self, value):
    ...

Dışarıdan kullanılan ad price ise getter metodunun adı, @price.setter dekoratöründeki ad ve setter metodunun adı aynı olmalıdır. Setter metoduna farklı bir ad vermek bu konuda en sık yapılan hatalardandır.

__init__ neden setter’ı kullanıyor?

Product.__init__ içinde doğrudan:

self._price = price

yazmak yerine:

self.price = price

yazdık. Böylece başlangıçtaki fiyat ile daha sonraki fiyat atamaları aynı doğrulama kuralından geçer.

class Product:
    def __init__(self, price):
        self.price = price  # setter çalışır

    @property
    def price(self):
        return self._price

    @price.setter
    def price(self, value):
        if value <= 0:
            raise ValueError("Fiyat pozitif olmalı")
        self._price = value

Bu, doğrulamayı iki yere kopyalamaktan daha güvenlidir: kural değişirse yalnız setter’ı güncellersiniz.

ImportantTek doğrulama noktası

Aynı kuralı __init__ içinde bir kez, setter içinde bir kez daha yazarsanız ikisi kolayca birbirini tutmaz hâle gelir: birini güncelleyip ötekini unutursunuz. Niteliği dışarıdan değiştirmenin yolu setter ise __init__ de mümkün olduğunda ilk değeri o setter’la atasın.

Fail fast

Fail fast (erken hata ver) ilkesi şunu söyler: geçersiz bir değer ortaya çıktığında sorunu mümkün olduğunca erken bildirmek genellikle daha güvenlidir. Örneğin

Product("Klavye", -100)

çağrısının daha nesne oluşturulurken hata vermesi, negatif fiyatın programın içinde dolaşıp çok daha sonra yanlış bir rapora yol açmasından iyidir.

Yarım güncelleme tehlikesi

def transfer_to(self, other, amount):
    self._balance -= amount
    if self._balance < 0:
        raise ValueError("Yetersiz bakiye")
    other._balance += amount

Bakiye önce azaltılıyor, kontrol sonra yapılıyor. Kontrol hata verdiğinde self._balance çoktan eksiye düşmüş oluyor ve öyle kalıyor; para other hesabına da geçmiyor.

Bir işlemin yapılabilmesi için önceden doğru olması gereken koşula ön koşul denir. Burada ön koşul, bakiyenin tutara yetmesidir. Daha güvenli sıra şudur:

  1. Tüm ön koşulları kontrol et,
  2. Sonra durumu değiştir.

Bu ilkeyi dönem boyunca kullanacağız.

Alıştırma: Invariant belirle

Aşağıdaki sınıflar için en az iki invariant yazın:

  • Student
  • Product
  • Reservation

Tarihlerin sırası, puan aralığı, fiyatın pozitif ya da stoğun negatif olmaması gibi kurallar düşünebilirsiniz.

Alıştırma: Güvenli withdraw

if amount <= 0:
    raise ValueError("Tutar pozitif olmalı")
if amount > self._balance:
    raise ValueError("Yetersiz bakiye")

Alıştırma: Property mi metot mu?

Aşağıdakileri tartışın:

  • account.balance
  • account.withdraw(100)
  • rectangle.area
  • order.cancel()

Hangileri nesnenin verisinden hesaplanan bir değer gibi, hangileri nesneye verilen bir komut gibi okunuyor? Python’da her şeyi property yapmanız gerekmez, her değeri bir get_...() metoduyla okumanız da gerekmez. Arayüzü, kodu okuyan kişi neyin değer neyin eylem olduğunu hemen anlayacak biçimde kurun.

Sıra sizde: Güvenli ürün

Product sınıfı yazın:

  • name: boş olamaz,
  • price: 0’dan büyük olmalı,
  • stock: negatif olamaz.

Metotlar:

  • add_stock(amount)
  • sell(amount)

Fiyatı getter ve setter’ı olan bir property ile yazın. __init__ içinde self.price = price yazarak aynı doğrulamayı yeniden kullanın. Stok yetmezse sell hata vermeli ve stoğu değiştirmemelidir.

Çalışır kod görevi: En az şu beş durumu çalıştırın: geçerli bir ürün, geçersiz başlangıç fiyatı, sonradan atanan geçersiz fiyat, başarılı satış ve yetersiz stok. Hata veren işlemlerden sonra niteliklerin değişmediğini assert ile doğrulayın.

Sınav provası

Önce kendi cevabınızı seçin; sonra cevap anahtarında her şıkkın neden doğru veya yanlış olduğunu okuyun. Çeldiriciler uydurma değil, bu konuda gerçekten yapılan hatalardır. Sınav maddeleri de bu mantıkla yazılır.

Madde 1. Değişmez koşul (invariant) nedir?

  • A. Sınıfın bütün örneklerinde ortak olan sınıf niteliğidir
  • B. Nesne kullanılırken her zaman doğru olması gereken kuraldır
  • C. Metotların kaç parametre alacağını belirleyen sözleşmedir
  • D. Nesne üretildikten sonra hiç değişmeyen nitelik değeridir
  • E. Programın hiç hata vermeyeceğini garanti eden kontroldür

Madde 2. Aşağıdaki program ne yazdırır?

class BankAccount:
    def __init__(self, owner, balance=0):
        if balance < 0:
            raise ValueError("Başlangıç bakiyesi negatif olamaz")
        self.owner = owner
        self.balance = balance

try:
    account = BankAccount("Ada", -100)
except ValueError as error:
    print(error)
  • A. 0; negatif değer varsayılana düşer
  • B. Hiçbir şey yazmaz; nesne balance = -100 ile oluşur
  • C. Program çöker; __init__ içinde raise kullanılamaz
  • D. None
  • E. Başlangıç bakiyesi negatif olamaz; nesne hiç oluşmaz

Madde 3. “Fail fast” ilkesi nesne tasarımında neyi savunur?

  • A. Geçersiz durumu olabildiğince erken bildirmeyi
  • B. Doğrulamayı nesne kullanıldığı ana ertelemeyi
  • C. Doğrulamaları kaldırarak programı hızlandırmayı
  • D. Bütün hataların except Exception ile yakalanmasını
  • E. Programın hata anında hemen sonlanmasını

Madde 4. Python’da kapsülleme temel olarak neyi amaçlar?

  • A. Niteliklere dışarıdan erişimi teknik olarak imkânsız kılmayı
  • B. Bütün nitelikleri salt okunur yapmayı
  • C. Niteliklerin yalnız sınıfın kurala uyan metotlarıyla değişmesini
  • D. Nesneyi kopyalanamaz hâle getirmeyi
  • E. Sınıfın metot sayısını azaltmayı

Madde 5. Aşağıdaki program çalıştırıldığında ne olur?

class Account:
    def __init__(self, balance):
        if balance < 0:
            raise ValueError("Bakiye negatif olamaz")
        self._balance = balance

acc = Account(100)
acc._balance = -50
print(acc._balance)
  • A. ValueError verir, çünkü __init__ içindeki kural her atamada yeniden çalışır
  • B. -50 yazdırılır, çünkü kural yalnız __init__ içinde denetleniyor
  • C. AttributeError verir, çünkü _balance private bir niteliktir
  • D. 100 yazdırılır, çünkü alt çizgili nitelik dışarıdan değiştirilemez
  • E. -50 yazdırılır ve Python ekrana bir uyarı basar

Madde 6. _balance adındaki tek alt çizgi ne anlatır?

  • A. Python’ın private erişim belirtecidir ve dışarıdan okumayı engeller
  • B. Niteliğin property ile sarmalandığını bildirir
  • C. Sınıfın iç ayrıntısı olduğunu bildiren bir adlandırma geleneğidir
  • D. Niteliğin ad dönüştürmesine gireceğini gösterir
  • E. Niteliğin sınıf düzeyinde tanımlandığını gösterir

Madde 7. Aşağıdaki sınıfta product.price = -50 satırı ne yapar?

class Product:
    def __init__(self, name, price):
        self.name = name
        self.price = price

    @property
    def price(self):
        return self._price

    @price.setter
    def price(self, value):
        if value <= 0:
            raise ValueError("Fiyat pozitif olmalı")
        self._price = value
  • A. Yeni bir price örnek niteliği oluşturur ve property’yi gölgeler
  • B. AttributeError yükseltir; price salt okunurdur
  • C. Hiçbir şey yapmaz; atama yok sayılır
  • D. Setter çalışır ve ValueError yükseltir; _price değişmez
  • E. _price değerini hata vermeden -50 yapar

Madde 8. price adlı bir property yazarken hangi üç ad aynı olmalıdır?

  • A. __init__ parametresinin adı, getter’ın adı ve dosyanın adı
  • B. Getter metodunun adı, iç niteliğin adı ve parametrenin adı
  • C. Sınıfın adı, property’nin adı ve iç niteliğin adı
  • D. Yalnız iki ad aynı olmalıdır; setter serbestçe adlandırılabilir
  • E. Getter metodunun adı, @price.setter dekoratöründeki ad ve setter metodunun adı

Madde 9. __init__ içinde self._price = price yerine self.price = price yazmanın gerekçesi nedir?

  • A. Başlangıç değeri de setter’dan geçer
  • B. Nesne oluşturmayı hızlandırır
  • C. Property tanımını zorunlu kılar
  • D. Nesneyi değiştirilemez yapar
  • E. _price adını dışarıdan gizler

Madde 10. Aşağıdaki metot neden güvenli değildir?

def transfer_to(self, other, amount):
    self._balance -= amount
    if self._balance < 0:
        raise ValueError("Yetersiz bakiye")
    other._balance += amount
  • A. Kontrol değişiklikten sonra yapıldığı için bakiye eksiye düşmüş kalır
  • B. raise bir metot içinde kullanılamaz
  • C. amount negatif olabileceği için tür denetimi gerekir
  • D. Sorun yoktur; istisna yükselince değişiklik kendiliğinden geri alınır
  • E. other nesnesinin özel niteliğine erişilemez

Madde 11. Durumu birden çok adımda değiştiren bir metotta hangi sıra izlenmelidir?

  • A. Önce durum değiştirilir, sonuç yanlışsa geri alınır
  • B. Kontrol hiç yapılmaz; çağıran doğru değer göndermekle yükümlüdür
  • C. Kontroller try/except içine alınır ve hata yutulur
  • D. Önce bütün ön koşullar sınanır, sonra durum değiştirilir
  • E. Her adımdan sonra ayrı ayrı kontrol yapılır

Madde 1. Doğru: B. balance >= 0 ya da 0 <= score <= 100 gibi kurallar nesne kullanıldığı sürece doğru kalmalıdır. Niteliklerin değeri değişir, kural değişmez.

  • A yanlış: Sınıf niteliği paylaşılan veridir; kural değildir.
  • C yanlış: Metodun kaç parametre aldığını imzası belirler; bu ayrı bir konudur.
  • D yanlış: Hiç değişmeyen değer sabittir; invariant’ta ise değer değişebilir, değişmeyen kuraldır.
  • E yanlış: Kuralı korumanın yolu çoğu zaman hata yükseltmektir; amaç geçersiz değeri engellemektir.

Madde 2. Doğru: E. raise çalıştığı anda __init__ yarıda kesilir ve account adına hiçbir şey bağlanmaz. Nesne ilk andan itibaren geçerli olmalıdır.

  • A yanlış: Kod değeri kendiliğinden düzeltmez; hata yükseltir.
  • B yanlış: if kontrolü raise ile nesnenin oluşmasını engeller.
  • C yanlış: __init__ içinde hata yükseltmek olağan ve önerilen bir kalıptır.
  • D yanlış: Yazdırılan, yakalanan istisnanın mesajıdır.

Madde 3. Doğru: A. Product("Klavye", -100) çağrısının hemen hata vermesi, negatif fiyatın sistemde dolaşmasından iyidir. Hata ortaya çıktığı yere yakın bildirilirse nedenini bulmak kolaylaşır; geç fark edilen yanlış değer ise ilerideki raporları bozar.

  • B yanlış: Değeri kullanıldığı yerde denetlemek de akla yatkın görünebilir. Ama ilke tam da bu ertelemeye karşıdır.
  • C yanlış: “Fast” sözcüğü hızı çağrıştırdığı için bu anlam akla gelebilir. Ama tam tersi savunulur: doğrulama erkene alınır, kaldırılmaz.
  • D yanlış: Hatalarla çabuk başa çıkmanın yolu hepsini yakalamak gibi görünebilir. Ama her hatayı yakalamak sorunu gizler; bu ilkeye aykırıdır.
  • E yanlış: “Fail fast” adı, programın hata anında hemen çökmesi gerektiği izlenimini verir. Ama istisna yakalanıp işlenebilir, program sonlanmak zorunda değildir. İlke yalnız sorunun erken bildirilmesini ister.

Madde 4. Doğru: C. Python erişimi engellemez. Kuralı, niteliği değiştiren metotlar ve alt çizgi geleneğine uyan programcılar korur.

  • A yanlış: Python’da böyle bir engel yoktur; _balance adına da __balance adına da dışarıdan ulaşılabilir.
  • B yanlış: Çoğu nesnenin nitelikleri değişmek zorundadır; denetlenen, nasıl değiştikleridir.
  • D yanlış: Kopyalama ayrı bir konudur.
  • E yanlış: Kapsülleme çoğu zaman metot ekler.

Madde 5. Doğru: B. Tek alt çizgi erişimi engellemez: _balance bir adlandırma geleneğidir, Python bunu zorlamaz. Kontrol yalnız nesne oluşturulurken bir kez çalıştı. Dışarıdan yapılan doğrudan atama bu kontrolden geçmez. Kuralın her değişiklikte denetlenmesi için değişikliği bir metotla ya da property ile yapmak gerekir.

  • A yanlış: Kural sınıfın içinde yazıldığı için niteliği her zaman koruduğu düşünülebilir. Ama __init__ yalnız nesne oluşturulurken bir kez çalışır. Sonraki atamalar onu yeniden çalıştırmaz.
  • C yanlış: Alt çizgi “dokunmayın” uyarısı gibi okunduğu için erişimi engellediği sanılabilir. Ama Python’da dışarıdan erişimi engelleyen bir private belirteci yoktur; alt çizgi yalnız niyeti gösterir.
  • D yanlış: Alt çizgili ad korunuyormuş gibi göründüğü için atamanın etkisiz kaldığı sanılabilir. Ama atama başarılı olur ve değer gerçekten -50 olur.
  • E yanlış: Geleneğe aykırı bir kullanımın en azından uyarı vereceği beklenebilir. Ama Python alt çizgili bir ada yapılan atama için uyarı vermez. Geleneğe uymak programcıya kalır.

Madde 6. Doğru: C. Alt çizgi yalnız niyeti gösterir; Python erişimi teknik olarak engellemez. Bu geleneğe uyulmasını Python zorlamaz; kodu yazan ekip kendi arasında uyar.

  • A yanlış: Alt çizgi başka dillerdeki private sözcüğünün karşılığıymış gibi anlatıldığında bu yanılgı doğar. Python’da böyle bir erişim belirteci yoktur; dışarıdan okuma da yazma da yapılabilir.
  • B yanlış: Alt çizgili ad çoğunlukla bir property arkasında durduğu için bu bağ kurulabilir. property ile birlikte sık kullanılır, ama alt çizgi tek başına bunu söylemez.
  • D yanlış: Tek ve çift alt çizgi kolayca karıştırılır. Ad dönüştürmesi çift alt çizgide olur.
  • E yanlış: Alt çizgi sınıfa ait bir işaret gibi görünebilir. Ama niteliğin nesneye ait olduğunu self. ile atanması gösterir.

Madde 7. Doğru: D. property sayesinde normal görünen atama bir metot çağrısına dönüşür ve doğrulamadan geçer.

  • A yanlış: Python price adına yapılan atamayı setter’a gönderir; nesnede aynı adlı yeni bir nitelik oluşmaz, gölgeleme olmaz.
  • B yanlış: Setter tanımlı olduğu için atama yapılabilir.
  • C yanlış: Atama setter’ı çağırır ve hata yükselir.
  • E yanlış: Setter bunu engeller.

Madde 8. Doğru: E. Üçü de dışarıdan kullanılan price adını taşır. Setter’a farklı ad vermek en sık yapılan hatalardandır.

  • A yanlış: Dosya adının bir ilgisi yoktur.
  • B yanlış: İç nitelik genellikle _price olur; parametre adı serbesttir.
  • C yanlış: Sınıf adının property adıyla ilgisi yoktur.
  • D yanlış: Setter farklı adla yazılırsa property’ye bağlanmaz ve atama çalışmaz.

Madde 9. Doğru: A. Başlangıç değeri de setter’daki doğrulamadan geçer ve kural tek yerde kalır. Aynı kuralı iki yerde yazarsanız zamanla ikisi birbirini tutmayabilir. Kural tek yerdeyse değiştiğinde yalnız orayı güncellersiniz.

  • B yanlış: self.price daha sade göründüğü için daha hızlı sanılabilir. Tersine, araya bir metot çağrısı girer; gerekçe hız değildir.
  • C yanlış: Satır bir property kullandığı için onu tanımlatan şey sanılabilir. Ama property zaten tanımlıdır; bu satır onu kullanır.
  • D yanlış: Doğrulama nesneyi koruduğu için onu kilitlediği düşünülebilir. Ama setter niteliği kurala uyarak değiştirmek için vardır; nesneyi değiştirilemez yapmaz.
  • E yanlış: Alt çizgili ad satırdan kalktığı için gizlenmiş gibi görünür. Ama ad yine erişilebilir; kazanç, doğrulamanın tek yerde kalmasıdır.

Madde 10. Doğru: A. Hata yükseldiğinde self._balance zaten negatife düşmüştür; nesne yarım güncellenmiş, geçersiz bir durumda kalır. Doğru sıra önce bütün ön koşulları sınamak, sonra durumu değiştirmektir.

  • B yanlış: raise örnekleri çoğunlukla fonksiyonlarda görüldüğü için metotta kullanılamayacağı sanılabilir. Ama kullanılabilir; sorun raise satırının değişiklikten sonra gelmesidir.
  • C yanlış: Negatif amount gerçekten ayrı bir eksik olduğu için cazip gelir. Ama bu bir tür denetimi değil, değer denetimidir; metodu güvensiz yapan da işlemlerin sırasıdır.
  • D yanlış: İstisna işlemi iptal ediyormuş gibi göründüğü için yapılan değişikliğin de geri alınacağı sanılabilir. Ama Python değişikliği kendiliğinden geri almaz; bakiye eksi kalır.
  • E yanlış: Alt çizgili bir niteliğe başka bir nesne üzerinden erişmek kurala aykırı görünebilir. Ama tek alt çizgi teknik bir engel değildir; aynı sınıf içinde bu erişim olağandır.

Madde 11. Doğru: D. Böylece hata yükseldiğinde nesne hiç değişmemiş olur ve geçerli durumunu korur.

  • A yanlış: Geri alma kodu kolayca eksik kalır ve nesne yarım güncellenmiş kalabilir.
  • B yanlış: İnvariant’ı korumak nesnenin kendi sorumluluğudur.
  • C yanlış: Hata gizlenir ve geçersiz durum kimse fark etmeden kalıcı olur.
  • E yanlış: İlk adım geçip ikincisi düşerse nesne yine yarım kalır.

Tek sayfa özet

  • Değişmez koşul (invariant), nesne için her zaman doğru olması gereken kuraldır; balance >= 0 gibi.
  • Kapsülleme, niteliklerin yalnız sınıfın kurallı metotlarıyla değişmesidir.
  • Kural hem __init__ içinde hem niteliği değiştiren her metotta denetlenmelidir.
  • _name bir erişim yasağı koymaz, yalnız “bu ad içeride kullanılır” der. Kuralı Python değil, sınıfın arayüzü korur.
  • property, product.price = 900 gibi sade bir atamanın arkasına kontrol koyar.
  • Getter, @price.setter ve setter aynı adı taşır. __init__ ilk değeri setter’la atarsa doğrulama tek yerde kalır.

Bu bölümün kazanımları

Bu bölümü bitiren öğrenci:

  • Bir sınıf için değişmez koşul (invariant) yazar.
  • Geçersiz başlangıç durumunu __init__ içinde engeller.
  • Kapsüllemede amacın erişimi yasaklamak değil, niteliklerin yalnız sınıfın metotlarıyla değişmesi olduğunu açıklar.
  • Tek alt çizginin bir adlandırma geleneği olduğunu ve Python’ın dışarıdan erişimi engellemediğini gösterir.
  • property ve setter tanımlar; üç adın aynı olması gerektiğini bilir.
  • __init__ içinde setter’ı kullanarak doğrulamayı tek yerde tutar.
  • Önce ön koşulları sınayıp sonra durumu değiştirerek yarım güncellemeyi önler.
Back to top