Gözetimli bütünleştirici nesne tasarımı

Son haftada yeni bir OOP kavramı yok. Dönem boyunca öğrendiklerinizi, kapsamı belli küçük bir değişiklikte birlikte kullanacaksınız: bir nesnenin o anki durumunu okumak, her kuralı doğru sınıfa koymak, nesnenin geçersiz bir duruma düşmesini önlemek, iki nesne arasındaki bağı tutarlı tutmak ve değişikliğin başka neleri etkilediğini açıklamak.

Bu bölümün kapsamı

NoteSınavda sorulur
  • Bir sistemi okurken hangi sınıfın hangi durumu koruduğunu belirleme
  • Birden çok nesneyi değiştiren işlemde nesneler arası tutarlılık
  • in ve list.remove() işlemlerinin == eşitliğini kullanması
  • __eq__ eklemenin koleksiyon davranışını da değiştirmesi
  • İnvariant için raise, geliştirici kontrolü için assert
  • Bir iş kuralının hangi sınıfa ait olduğuna, kuralın koruduğu durumun sahibine bakarak karar verme
  • Tasarım değişikliğini regresyon kontrolleriyle doğrulama
  • Transaction (birkaç değişikliği ya hep birlikte ya hiç uygulayan düzenek) ve geri alma (rollback)
  • Kalıcı veri saklama ve eşzamanlılık
  • pytest / unittest ile otomatik test yazımı

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

Yeni gereksinim hangi sınıfın sorumluluğuna ait?

Aşağıda çalışan küçük bir kütüphane modeli var: Book, Member ve Library. Bu modele üç gereksinim ekleyeceğiz ve dönem boyunca verdiğimiz kararları aynı problemde kullanacağız. Yazacağınız kod kısa. Zorlanacağınız yer, her kuralı doğru sınıfa koymak ve bir kitap ödünç verildiğinde ya da iade edildiğinde kitabın ve üyenin kayıtlarını birbiriyle uyumlu bırakmak.

Başlangıç sistemi

WarningBaşlangıç kodu öğretim için küçük tutuldu

Gerçek bir kütüphane sistemi verileri kalıcı olarak saklar ve aynı anda birden çok işlemi yürütür. O durumda birden fazla nesneyi değiştiren işlemler için transaction gibi daha güçlü düzenekler gerekebilir. Bu model bunları dışarıda bırakır. Amacı, önce doğrula → sonra değiştir ilkesini ve her sınıfın hangi işten sorumlu olduğunu açıkça göstermektir.

Önce sistemi okuyun

Kod eklemeden şu soruları cevaplayın:

Problem alanındaki farklı sorumlulukların uygun sınıflara dağıtılmasını gösteren tasarım diyagramı.

Nesne sorumlulukları
  1. Book hangi durumu koruyor?
  2. Member hangi koleksiyonu yönetiyor ve dışarıya neden listenin kendisini değil de bir tuple kopyasını veriyor?
  3. Library neden kitap ve üyeleri sözlükte tutuyor?
  4. Library.borrow() hangi işleri kendi yapıyor, hangilerini nesnelere devrediyor?
  5. Aynı Book nesnesi hem Library.books içinde hem üyenin koleksiyonunda bulunabilir mi? Bu iki ayrı kitap anlamına gelir mi?
  6. Hangi kontroller iki nesne değişmeden önce yapılmalıdır?

Nesneler arası tutarlılık

Her nesnenin kendi invariant’ını (her zaman doğru kalması gereken koşulunu) koruması yetmeyebilir. Ödünç alma hem Book hem Member nesnesini değiştirir. Kitap ödünçte diye işaretlendikten sonra üyenin listesine ekleme başarısız olursa kitap ödünçte görünür ama hiçbir üyenin listesinde yer almaz. Sistem yarım güncellenmiş kalır.

Bu yüzden Library.borrow() şu sırayı izler:

  1. Üye ve kitap bulunur,
  2. Üyenin ödünç alma koşulları kontrol edilir,
  3. Kitabın rafta olup olmadığı kontrol edilir,
  4. Ancak bundan sonra iki nesne de değiştirilir.
ImportantBir sınıfın invariant’ı yetmeyebilir

Bir işlem birden fazla nesneyi birlikte değiştiriyorsa, bu nesnelerin birbiriyle uyumlu kalması (nesneler arası tutarlılık) ayrıca düşünülmesi gereken bir tasarım sorusudur.

in ve remove() hangi eşitliği kullanıyor?

Şu iki satıra dikkat edin:

if book in self._borrowed_books:
    ...

self._borrowed_books.remove(book)

in ile listede eleman aramak ve list.remove() ile eleman silmek, eşleşen elemanı == ile bulur. Başlangıç sisteminde Book sınıfında __eq__ tanımlı değil. Bu yüzden ISBN’leri aynı olsa bile iki ayrı Book örneği eşit sayılmaz.

Şimdi 12. haftadaki gibi ISBN eşitliği ekleyelim:

class Book:
    ...

    def __eq__(self, other):
        if not isinstance(other, Book):
            return NotImplemented
        return self.isbn == other.isbn

Artık:

first = Book("978-1", "Python")
second = Book("978-1", "Aynı ISBN, başka nesne")

print(first is second)  # False
print(first == second)  # True

Bu yüzden second in [first] de True döner.

Warning__eq__ eklemek yalnızca a == b satırını değiştirmez

Eşitliği tanımladığınızda in, list.remove(), iki listenin == ile karşılaştırılması ve başka koleksiyon işlemleri de bu tanımı kullanır, davranışları değişebilir. Book.__eq__’yi ISBN’e göre tanımladığınız anda Member sınıfındaki book in self._borrowed_books kontrolü ve remove() çağrısı da, bu satırlara hiç dokunmadığınız hâlde farklı davranmaya başlar.

Bu her zaman kötü değildir; kütüphane modeliniz için istediğiniz davranış bu olabilir. Ama bu etkiyi bilerek test etmeniz gerekir.

Mikro deney: eşitliğin yan etkisini görün

Listede probe ile aynı nesne yok. Yine de remove(probe), ISBN’i aynı olduğu için eşit sayılan registered nesnesini listeden siler. Modelinizde bunun doğru olup olmadığı bir tasarım kararıdır; bu kararı bilerek verin.

assert ne için kullanılmalı?

Ders boyunca bir noktada neyin doğru olmasını beklediğimizi koda yazmak için şöyle satırlar kullanabiliriz:

assert book.borrowed_by is member
assert len(member.borrowed_books) == 1

assert, koşul yanlışsa AssertionError verir. Bu tür kontroller test yazmaya doğru atılmış iyi bir ilk adımdır.

Ama program gerçek kullanıcılara hizmet verirken (üretimde) gelen girdileri assert ile doğrulamayın. Şu satır bir iş kuralını korumaya çalışıyor ve bu güvenli değil:

assert amount > 0, "Tutar pozitif olmalı"

Python -O (optimize) seçeneğiyle çalıştırıldığında assert satırları kaldırılabilir; o zaman kural da ortadan kalkar.

ImportantInvariant için raise, geliştirici kontrolü için assert

Kullanıcı girdisini, bir iş kuralını ya da nesnenin geçerli durumunu koruyan kontrol programın normal işleyişinin parçasıdır; bunun için if ...: raise ... kullanın. assert ise geliştirirken ya da test ederken “programcı olarak burada doğru olduğunu varsaydığım şey gerçekten doğru mu?” diye sorduğunuz kontroller içindir.

Gereksinim 1: Üye başına en fazla üç kitap

MAX_BORROWED_BOOKS = 3 kuralı için en uygun yer Member sınıfıdır, çünkü üyenin ödünç aldığı kitapların listesini o tutar. İşlemi yöneten Library de iki nesneyi değiştirmeden önce member.ensure_can_borrow(book) çağırır. Böylece sınır doluysa hiçbir nesne değişmeden işlem durur ve yarım güncelleme riski azalır.

Şunları test edin:

  • 1., 2. ve 3. kitap başarıyla alınır,
    1. kitap reddedilir,
  • Reddedilen kitap hâlâ ödünç alınabilir durumdadır,
  • Üyenin koleksiyonunda yalnızca üç kitap vardır.

Gereksinim 2: İade işlemi

Bir kullanım senaryosunda her nesnenin kendi sorumluluğundaki işi yaparak metot çağrılarıyla diğerleriyle işbirliği yaptığını gösteren akış.

Nesneler arası işbirliği

İade kodunu yazmadan önce şu durumları düşünün:

  • Üye yok,
  • Kitap yok,
  • Kitap zaten kütüphanede,
  • Kitap başka bir üyede,
  • Kitabın borrowed_by niteliği üyeyi gösteriyor ama kitap üyenin listesinde yok.

Başarılı bir iadeden sonra şu iki koşul doğru olmalı:

assert book.is_available
assert book not in member.borrowed_books

Bu assertlar test kontrolüdür; return_book() içindeki iş kurallarının yerini tutmaz.

Gereksinim 3: ISBN’e göre eşitlik

Book sınıfına nesneleri ISBN’e göre karşılaştıran bir __eq__ ekleyin. Sonra aşağıdaki regresyon kontrollerini çalıştırın. Regresyon kontrolü, bir değişiklikten sonra eskiden çalışan davranışın bozulup bozulmadığını ya da nasıl değiştiğini sınar:

  1. Aynı ISBN’li iki farklı nesne == ile eşit, is ile farklı olmalı.
  2. Aynı ISBN’li ikinci nesne, üyelik kontrolünde ilk nesneyle eşleşmeli.
  3. remove() davranışının artık ISBN eşitliğinden etkilendiğini gösterin.
  4. Bu davranışı kütüphane modelinizde isteyip istemediğinizi kısaca gerekçesiyle yazın.

Bu görevde 6. haftanın kimlik (is), 9. haftanın nesne koleksiyonları ve 12. haftanın eşitlik (==, __eq__) konularını birlikte kullanırsınız.

Gözetimli bütünleştirici görev

Başlangıç sistemine şu küçük değişiklikleri uygulayın:

  • Üye başına en fazla üç kitap kuralını doğrulayın,
  • Güvenli iade davranışını tamamlayın,
  • Book.__eq__’yi ISBN’e göre tanımlayın,
  • Member’ın iç listesinin dışarıdan doğrudan değiştirilmesini engelleyin,
  • Başarılı ve başarısız işlemleri küçük kontrollerle doğrulayın.

Çalışır kod görevi. En az şu senaryoları otomatik olarak kontrol eden çalışır bir dosya hazırlayın:

  • Normal ödünç alma,
  • Aynı kitabı tekrar alma,
  • Dördüncü kitap sınırı,
  • Başarılı iade,
  • Yanlış üyeden iade,
  • Aynı ISBN’li farklı Book örnekleriyle eşitlik/üyelik davranışı.

İş kurallarını assert ile değil raise ile koruyun; assertları yalnızca testlerde sonucu doğrulamak için kullanın.

Kısa tasarım savunması

Kodun altına şu sorular için en fazla 150 kelimelik bir cevap yazın:

  1. Ödünç alma sınırı neden Member sınıfının sorumluluğudur?
  2. Library ön koşulları neden yine de değişiklikten önce sırayla kontrol eder?
  3. Book.__eq__ eklemek koleksiyon davranışını nasıl değiştirdi?
  4. Member’ın iç listesi neden dışarıya değiştirilebilir (mutable) bir liste olarak verilmedi?
  5. Hangi kontrolleri raise, hangilerini assert ile yazdınız ve neden?

Deneme sınavı: 25 madde

Bu 25 madde final biçiminin birebir örneğidir: final de 25 maddedir, her madde beş şıklıdır ve her kazanımdan en fazla bir madde gelir. Önce kendiniz çözün, sonra cevap anahtarındaki gerekçeleri okuyun.

1. C’de int age = 20; yazarken Python’da yalnızca age = 20 yazmak neyi anlatır?

  • A. Tür yalnızca print() çağrısında belirlenir
  • B. Python age adını kendiliğinden int olarak bildirir ve başka tür atanamaz
  • C. Python’da tür kavramı yoktur; bütün değerler aynı biçimde saklanır
  • D. Python bütün sayıları float olarak tutar, bu yüzden bildirim gerekmez
  • E. Değerin bir türü vardır; tür bilgisi değişken adına değil, adın bağlandığı değere aittir

2. Aşağıdaki program score = 70 iken ne yazdırır?

if score >= 85:
    result = "AA"
elif score >= 70:
    result = "BB"
else:
    result = "Başarısız"

print(result)
  • A. BB
  • B. Hiçbir şey yazmaz
  • C. BB ve Başarısız
  • D. Başarısız
  • E. AA

3. Bir metindeki benzersiz kelimeleri tutmak için en uygun koleksiyon hangisidir?

  • A. tuple; değiştirilemez olduğu için tekrar eklenemez
  • B. list; sıra korunduğu için tekrar sorunu oluşmaz
  • C. str; kelimeler zaten metin olduğu için
  • D. set; aynı değeri yalnız bir kez tutar
  • E. dict; anahtarlar sıralı tutulduğu için

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

def show_square(x):
    print(x * x)

first = show_square(5)
print("first:", first)
  • A. Önce 25, sonra first: 25
  • B. Yalnızca first: 25
  • C. Yalnızca first: None
  • D. Önce 25, sonra first: None
  • E. Hata verir; dönüş değeri olmayan bir çağrı bir değişkene atanamaz

5. with open("notes.txt", "r", encoding="utf-8") as file: yazımının elle open() / close() çiftine göre avantajı nedir?

  • A. Dosya yoksa kendiliğinden oluşturur
  • B. Kodlama belirtmeyi gereksiz kılar
  • C. Dosyanın içeriğini kendiliğinden satırlara böler
  • D. Hata oluşsa bile blok bitince dosya kapatılır
  • E. Dosyayı belleğe tümüyle yükleyerek okumayı hızlandırır

6. score = 80 ve ardından score = 90 yazıldığında ne olur?

  • A. score adlı kutunun içindeki 80 değeri 90 ile üzerine yazılır
  • B. İki ayrı score değişkeni oluşur
  • C. score adı önce bir nesneye, sonra başka bir nesneye bağlanır
  • D. Hata verir; bir ada iki kez atama yapılamaz
  • E. 80 değeri bellekten hemen silinir ve score geçersiz olur

7. Sınıf ile nesne (örnek) arasındaki fark nedir?

  • A. Sınıf yalnızca veriyi, nesne yalnızca davranışı tutar
  • B. Nesne sınıfın kopyasıdır; sınıf değişirse nesne değişmez
  • C. Her sınıftan yalnızca bir nesne oluşturulabilir
  • D. Sınıf program çalışırken, nesne kod yazılırken var olur
  • E. Sınıf tanımı verir; nesne o tanımdan oluşturulan örnektir

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

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

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

class Customer:
    def __init__(self, name):
        self.name = name

class Order:
    def __init__(self, customer):
        self.customer = customer

customer = Customer("Ada")
order = Order(customer)
customer.name = "Ece"

print(order.customer.name)
  • A. Customer
  • B. Ada; nesne __init__ sırasında kopyalanır
  • C. Ece; order.customer aynı Customer nesnesini gösterir
  • D. AttributeError; order.customer.name iki adımlı erişim geçersizdir
  • E. None

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

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

class Order:
    def __init__(self):
        self._items = []

    def add_product(self, product):
        self._items.append(product)

    def total(self):
        return sum(product.price for product in self._items)

order = Order()
order.add_product(Product("Klavye", 1000))
order.add_product(Product("Mouse", 500))
print(order.total())
  • A. 2
  • B. 1500
  • C. TypeError; nesneler doğrudan toplanamaz
  • D. None
  • E. 0

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

class Employee:
    def __init__(self, name):
        self.name = name

    def describe(self):
        return f"Çalışan: {self.name}"

class Developer(Employee):
    pass

dev = Developer("Ada")
print(dev.describe())
print(isinstance(dev, Employee))
  • A. Geliştirici: Ada ve True
  • B. TypeError; pass gövdeli bir sınıf örneklenemez
  • C. Çalışan: Ada ve True
  • D. AttributeError ve True
  • E. Çalışan: Ada ve False

12. Kalıtım kararı verirken is-a sezgisinden daha güçlü olan soru nedir?

  • A. Alt sınıf, üst sınıfın __init__ metodunu kullanabiliyor mu
  • B. Alt sınıf, üst sınıfın beklendiği her yerde sorunsuz çalışıyor mu
  • C. Alt sınıf üst sınıfla aynı dosyada mı
  • D. Alt sınıf, üst sınıfa yeni metotlar ekleyerek onu genişletiyor mu
  • E. İki sınıfta aynı adlı ortak nitelikler bulunuyor mu

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

class EmailNotification:
    def send(self, message):
        return f"E-posta: {message}"

class SmsNotification:
    def send(self, message):
        return f"SMS: {message}"

for notification in [EmailNotification(), SmsNotification()]:
    print(notification.send("Merhaba"))
  • A. TypeError; farklı türler aynı listede tutulamaz
  • B. Hiçbir şey yazmaz
  • C. E-posta: Merhaba ve SMS: Merhaba
  • D. İki satırda da E-posta: Merhaba
  • E. AttributeError; iki sınıfın ortak üst sınıfı yoktur

14. EmailNotification ve SmsNotification ortak bir üst sınıftan türemedikleri hâlde aynı döngüde kullanılabiliyor. Bunun nedeni nedir?

  • A. Python’un örtük olarak ortak bir üst sınıf oluşturması
  • B. Duck typing; çağrı için yalnız gereken metodun bulunması yeter
  • C. Metot adlarının aynı olması hâlinde Python’ın sınıfları birleştirmesi
  • D. isinstance() denetiminin listede kendiliğinden yapılması
  • E. İki sınıfın da object sınıfından türemesi

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

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

    def __str__(self):
        return f"{self.code} - {self.name}"

print(Product("P1", "Klavye"))
  • A. P1 - Klavye
  • B. Product(code='P1', name='Klavye')
  • C. TypeError; print() bir nesne alamaz
  • D. <__main__.Product object at 0x...>
  • E. None

16. Bir sınıfta yalnız __str__ tanımlanırsa repr(obj) ne verir?

  • A. __str__ sonucunu verir; tanımlı tek temsil odur
  • B. AttributeError yükseltir; __repr__ tanımlı değildir
  • C. Boş metin verir
  • D. None verir
  • E. Varsayılan temsili verir; repr() __str__ metoduna bakmaz

17. Bir paket dizinindeki __init__.py dosyası hakkında doğru olan hangisidir?

  • A. Klasörü paket olarak işaretler ve boş bırakılabilir
  • B. İçi boş bırakılırsa paketteki modüller import edilemez
  • C. Yalnızca PyPI’ya yüklenecek paketlerde gerekir
  • D. Paketin bütün sınıflarını içermek zorundadır
  • E. Paketin ana giriş noktasıdır ve python paket ile çalıştırılır

18. Kendi yazdığınız shop paketi için ModuleNotFoundError alıyorsanız ilk olarak ne yapmalısınız?

  • A. Bütün import satırlarını try/except içine almak
  • B. pip install shop komutunu çalıştırmak
  • C. Python’ı kaldırıp güncel sürümle yeniden kurmak
  • D. Terminalin proje kökünde açık olup olmadığını denetlemek
  • E. Paketteki __init__.py dosyasını silip yeniden denemek

19. Member.borrowed_books neden iç listenin kendisini değil de bir demet (tuple) döndürür?

  • A. __eq__ karşılaştırmasını mümkün kılmak için
  • B. Çağıran iç listeyi doğrudan değiştiremesin diye
  • C. Kitap sayısının sabit olduğunu bildirmek için
  • D. Demetler listelerden daha hızlı olduğu için
  • E. Demet içindeki nesnelerin de değişmez olmasını sağlamak için

20. Ödünç alma işlemi hem Book hem Member durumunu değiştiriyorsa hangi sıra izlenmelidir?

  • A. İki güncelleme try/except içine alınır ve hata yutulur
  • B. Önce bütün kontroller yapılır, iki durum ancak sonra değiştirilir
  • C. Her nesne kendi kontrolünü kendi metodunda yapar ve sıra önemli değildir
  • D. Kontroller güncellemelerden sonra assert ile yapılır
  • E. Önce kitabın durumu değiştirilir, sonra üyede sorun çıkarsa geri alınır

21. if book in self._borrowed_books: ve self._borrowed_books.remove(book) satırları eşleşmeyi neye göre arar?

  • A. == eşitliğine, yani __eq__ metoduna göre
  • B. Elemanın listedeki sıra numarasına (indeksine) göre
  • C. Yalnızca nesne kimliğine (is) göre
  • D. __hash__ değerine göre, set aramasındaki gibi
  • E. Nesnenin __str__ metodunun döndürdüğü metne göre

22. Book.__eq__ metodunu ISBN’e göre tanımlamak yalnızca a == b satırını mı etkiler?

  • A. Evet; __eq__ yalnız açık karşılaştırma ifadelerinde kullanılır
  • B. Hayır; ayrıca is operatörünün davranışı da değişir
  • C. Hayır; __str__ çıktısı da değişir
  • D. Evet; ancak yalnız aynı sınıfın örnekleri karşılaştırılırsa
  • E. Hayır; in ve remove() gibi koleksiyon işlemleri de etkilenir

23. assert amount > 0, "Tutar pozitif olmalı" ile iş kuralı korumak neden güvenli değildir?

  • A. assert’ün yükselttiği hata yakalanamaz
  • B. assert yalnız True ya da False olan değişkenleri sınayabilir
  • C. assert ikinci bir mesaj parametresi kabul etmez
  • D. Python -O ile çalıştırılınca assert satırları atlanır
  • E. assert yalnız test_ ile başlayan test dosyalarında yazılabilir

24. MAX_BORROWED_BOOKS = 3 kuralının Member içinde tutulmasının gerekçesi nedir?

  • A. Sayısal sabitler her zaman sınıf düzeyinde tutulmalıdır
  • B. Library işlemi yönettiği için sabit tutamaz
  • C. Böylece kural çalışma sırasında değiştirilebilir
  • D. Python sabitleri yalnız sınıf içinde tanımlamaya izin verir
  • E. Kuralın sınırladığı ödünç listesini Member tuttuğu için

25. Üye başına en fazla üç kitap kuralı için hangi kontroller yapılmalıdır?

  • A. İlk üç kitabın alındığı ve 4. kitabın yan etkisiz reddedildiği
  • B. Yalnız 4. kitabın ValueError ile reddedildiği
  • C. Yalnız 4. denemede yazılan hata mesajının metni
  • D. Yalnız işlem sonunda üyenin koleksiyon uzunluğunun 3 olduğu
  • E. Yalnız MAX_BORROWED_BOOKS sabitinin değerinin 3 olduğu

1. Doğru: E. 20 bir int nesnesidir. age yalnızca o nesneye bağlanan addır; ad önceden bir türe bildirilmez.

  • A yanlış: Tür değerin kendisine aittir ve print() çağrılmasa da vardır.
  • B yanlış: Aynı ad sonradan age = "yirmi" ile bir str değere bağlanabilir; bildirim yoktur.
  • C yanlış: type(20) int verir. Tür vardır; ama tür bilgisi adda değil, değerin kendisinde durur.
  • D yanlış: type(20) int döndürür; int ve float ayrı türlerdir.

2. Doğru: A. İlk koşul yanlıştır; ikinci koşulda 70 >= 70 doğru olduğu için zincir orada durur.

  • B yanlış: else dalı da bulunduğu için result her durumda bir değere bağlanır.
  • C yanlış: elif zincirinde yalnızca bir dal çalışır; ilk doğru koşuldan sonra zincir biter.
  • D yanlış: else yalnızca önceki koşulların hepsi yanlışsa çalışır.
  • E yanlış: 70 >= 85 yanlıştır; ilk dal seçilmez.

3. Doğru: D. Küme tekrarları kendiliğinden eler ve “bu kelime var mı?” sorusuna doğrudan cevap verir. Sıra önemli değilse en doğrudan seçimdir.

  • A yanlış: Değiştirilemezliği “tekrar eklenemez” diye okumak kolaydır. Oysa değişmezlik tekrar içermediği anlamına gelmez; ("a", "a") geçerlidir.
  • B yanlış: Liste en tanıdık koleksiyon olduğu için ilk akla gelen seçimdir. Ama liste aynı değeri birden çok kez tutar; benzersizliği elle sağlamak gerekir.
  • C yanlış: Kelimeler zaten metin olduğu için ayrı bir yapıya gerek yokmuş gibi görünür. Ama metin, kelimeleri ayrı öğeler olarak tutan bir koleksiyon değildir.
  • E yanlış: Sözlük anahtarları da tekil olduğu için bu seçim işe yarar gibi görünür. Ama gerekçe yanlıştır: benzersizliği sıra değil, anahtarların tekil olması sağlar. Değer tarafı da boşa gider; yalnız üyelik için küme yeterlidir.

4. Doğru: D. İlk satırı fonksiyonun içindeki print() çağrısı yazdırır. Fonksiyon return içermediği için dönüş değeri Nonedır.

  • A yanlış: Fonksiyon 25 değerini ekrana yazar ama döndürmez; first adına bir sonuç gelmez.
  • B yanlış: Fonksiyon gövdesindeki print() de çalışır.
  • C yanlış: Çağrı sırasında gövdedeki print() çıktısı da görünür.
  • E yanlış: Atama geçerlidir; atanan değer Nonedır.

5. Doğru: D. with bloğu biterken dosyayı kendisi kapatır; blok içinde istisna yükselse de dosya kapanır. close() çağrısını unutma riski kalmaz.

  • A yanlış: with dosyayla ilgili işleri üstlendiği için dosyayı da hazırladığı sanılabilir. Ama dosyanın oluşup oluşmayacağını kip belirler; "r" kipinde hata yükselir.
  • B yanlış: with dosya ayarlarını kendisi yönetiyormuş gibi görünebilir. Ama encoding yine açıkça yazılmalıdır.
  • C yanlış: with ile açılan dosya çoğunlukla bir for döngüsüyle okunduğu için bölmeyi with yapıyor sanılabilir. Satırlara ayırmak ayrı bir iştir; for line in file ile yapılır.
  • E yanlış: Daha kısa bir yazımın daha verimli bir okuma getireceği düşünülebilir. Ama with okuma biçimini değiştirmez; yalnızca dosyanın kapanmasını üstlenir.

6. Doğru: C. Atama bir kutunun içeriğini değiştirmez; adın hangi nesneyi gösterdiğini değiştirir. İlk nesnenin içi değiştirilmez.

  • A yanlış: C’deki int score; bir bellek kutusu gibi çalıştığı için bu model akla yatkındır. Ama kutu modeli burada yanıltır; int nesneleri değiştirilemez ve içleri güncellenmez.
  • B yanlış: İki atama iki ayrı nesneyle çalıştığı için iki değişken varmış gibi görünür. Oysa tek bir ad vardır; yalnız bağlandığı nesne değişir.
  • D yanlış: Bir adın bir kez bağlandıktan sonra sabit kaldığı düşünülebilir. Oysa yeniden atama geçerli ve olağandır.
  • E yanlış: Eski değerin artık kullanılmaması, onun hemen silindiği izlenimini verir. Ama ad her zaman geçerli bir nesneyi gösterir; eski nesneye ne olacağı ise ayrı bir konudur.

7. Doğru: E. Sınıf, nesnelerin hangi nitelikleri ve metotları olacağını tanımlar. Nesne bu tanımdan oluşturulur ve kendi nitelik değerlerini taşır.

  • A yanlış: Nitelik ile metot ayrımı, sınıf ile nesne ayrımına yanlış eşlenebilir. Oysa sınıf hem nesnenin hangi nitelikleri taşıyacağını hem metotlarını tanımlar.
  • B yanlış: Nesnenin sınıftan “basıldığı” benzetmesi kopya fikrini doğurur. Ama örnek sınıfın kopyası değildir; metotlarını sınıfta bulur.
  • C yanlış: İlk örneklerde her sınıftan tek nesne oluşturulduğu için böyle bir sınır sanılabilir. Oysa bir sınıftan istediğiniz kadar nesne oluşturabilirsiniz.
  • D yanlış: İkisinin farklı zamanlarda var olduğu düşüncesi C’deki derleme anı ile çalışma anı ayrımından gelir. Oysa ikisi de program çalışırken vardır; class satırı da çalıştırılan bir deyimdir.

8. Doğru: C. 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.
  • B 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.

9. Doğru: C. Kompozisyonda nesne kopyalanmaz, referansı tutulur. customer.name değişince order.customer.name da yeni değeri verir.

  • A yanlış: Yazdırılan, sınıf adı değil nitelik değeridir.
  • B yanlış: Python argümanı kopyalamaz; aynı nesne paylaşılır.
  • D yanlış: Zincirleme nitelik erişimi geçerlidir.
  • E yanlış: name niteliği bir metne bağlıdır.

10. Doğru: B. _items Product nesnelerine referans tutar; total() her nesnenin price niteliğini toplar.

  • A yanlış: Toplanan fiyatlardır, eleman sayısı değil.
  • C yanlış: Toplanan nesneler değil, product.price değerleridir.
  • D yanlış: total() bir sayı döndürür.
  • E yanlış: İki ürün gerçekten eklenmiştir.

11. Doğru: C. Developer gövdesi boş olsa da __init__ ve describe üst sınıftan devralınır. Bir Developer nesnesi aynı zamanda bir Employee sayılır.

  • A yanlış: Developer metodu override etmemiştir; üst sınıfın metni kullanılır.
  • B yanlış: Gövdesi yalnızca pass olan sınıf geçerlidir; nesnesi oluşturulabilir.
  • D yanlış: describe üst sınıfta tanımlıdır ve bulunur.
  • E yanlış: isinstance() üst sınıfları da dikkate alır.

12. Doğru: B. Yerine geçme sorusu (Liskov ilkesi), üst sınıfın beklendiği yerde alt sınıf kullanıldığında sözleşmenin bozulup bozulmadığını sınar.

  • A yanlış: super().__init__() kalıtımla birlikte öğrenildiği için ölçüt gibi görünebilir. Ama kurucuyu kullanabilmek yerine geçme sorusunu cevaplamaz.
  • C yanlış: Yan yana yazılan sınıflar arasında bir bağ varmış gibi görünür. Ama sınıfların hangi dosyada durduğu tür ilişkisini değiştirmez.
  • D yanlış: Kalıtım “genişletme” diye anlatıldığı için yeni metot eklemek ölçüt gibi görünür. Ama metot eklemek, alt sınıfın üst türün yerine geçip geçemeyeceğini söylemez.
  • E yanlış: Ortak nitelikler iki sınıfın akraba olduğunu düşündürür. Ama iki sınıfta aynı nitelik bulunması, birini ötekinden türetmek için gerekçe olmaz.

13. Doğru: C. Döngü, nesnenin hangi sınıftan geldiğini bilmeden send() metodunu çağırır. EmailNotification e-posta metnini, SmsNotification SMS metnini döndürür.

  • A yanlış: Python listeleri karışık türleri tutabilir.
  • B yanlış: Döngü iki tur döner ve her turda yazdırır.
  • D yanlış: Çağrı, listedeki her nesnenin kendi sınıfındaki metoda gider.
  • E yanlış: Ortak üst sınıf gerekmez; iki nesnede de send metodu vardır.

14. Doğru: B. Nesnenin hangi sınıftan olduğuna değil, gereken metoda sahip olup olmadığına bakılır. İkisinde de send(message) metodu olduğu için aynı döngüde kullanılabilirler; aralarında kalıtım ilişkisi gerekmez.

  • A yanlış: Kalıtımdan sonra ortak davranışın hep bir üst sınıftan geldiği sanılabilir. Ama Python kendiliğinden ortak bir üst sınıf oluşturmaz.
  • C yanlış: Aynı adlı metotlar iki sınıfı birbirine bağlıyormuş gibi görünür. Ama sınıflar birleştirilmez; ayrı kalırlar.
  • D yanlış: Farklı türler aynı listede sorunsuz çalıştığı için arka planda bir tür denetimi yapıldığı sanılabilir. Döngü hiç tür denetimi yapmaz; yalnız metodu çağırır.
  • E yanlış: Her sınıfın object sınıfından türediğini bilen öğrenci ortak üst sınıfı burada arayabilir. Bu doğrudur ama object sınıfında send metodu yoktur.

15. Doğru: A. Sınıfta __str__ tanımlı olduğu için print() onun döndürdüğü metni yazar.

  • B yanlış: Bu bir __repr__ çıktısı biçimidir; burada __repr__ tanımlı değildir.
  • C yanlış: print() her nesneyi alır ve onu metne çevirerek yazar.
  • D yanlış: Bu varsayılan temsildir; yalnız __str__ de __repr__ de tanımlı değilse görünür.
  • E yanlış: __str__ bir metin döndürür ve print onu yazar.

16. Doğru: E. Yerine geçme tek yönlüdür: __str__ yoksa str() __repr__ sonucunu kullanabilir; ama repr() hiçbir zaman __str__ metoduna bakmaz. Yalnız __str__ tanımlamak repr davranışını değiştirmez.

  • A yanlış: Sınıfta tek temsil metodu olduğu için Python’un onu her yerde kullanacağı sanılabilir. Ama repr kendi varsayılanında kalır.
  • B yanlış: Sınıfta __repr__ yazılmadığı için metodun eksik olduğu düşünülebilir. Ama varsayılan __repr__ her sınıfta bulunur.
  • C yanlış: Temsil yazılmadığı için boş sonuç beklenebilir. Ama varsayılan temsil sınıfın adını ve nesnenin kimlik bilgisini (0x...) içerir.
  • D yanlış: Bir şey tanımlanmadığında sonuç None olacakmış gibi görünür. Ama repr() her zaman bir metin döndürür.

17. Doğru: A. Klasörün bir Python paketi olduğunu göstermenin alışılmış ve açık yoludur. İsterseniz içine paket ilk import edildiğinde çalışacak kodu ya da paketin dışarıya açacağı adları yazabilirsiniz. Şimdilik boş bırakmak yeterli.

  • B yanlış: Boş bir dosya işe yaramıyormuş gibi görünür. Ama boş bir __init__.py de klasörü paket olarak işaretlemeye yeter.
  • C yanlış: “Paket” sözcüğü pip install ile kurulan paketleri çağrıştırır. Ama __init__.py yerel paketlerde de kullanılır.
  • D yanlış: Dosya paketin kendisini temsil ediyormuş gibi göründüğü için paketin bütün kodunun burada toplandığı sanılabilir. Ama sınıflar ayrı modüllerde durur.
  • E yanlış: __init__ adı, kurucu gibi ilk çalışan şeyi çağrıştırır. Ama bir paketi python -m paket ile çalıştırmak için __main__.py kullanılır.

18. Doğru: D. Terminalin hangi klasörde olduğu ve app.py ile paketin aynı proje kökü altında bulunup bulunmadığı denetlenir. Kendi yazdığınız bir paket bulunamıyorsa eksik olan çoğu zaman bir kurulum değildir; program proje kökünden çalıştırılmamıştır.

  • A yanlış: Hata mesajı kaybolduğu için sorun çözülmüş gibi görünür. Ama hatayı gizler; nedeni ortadan kaldırmaz.
  • B yanlış: Hata mesajı “modül bulunamadı” dediği için eksik bir kurulum akla gelir. Ama paket sizin yazdığınızsa PyPI’da yoktur; aynı adlı yanlış bir paket kurulabilir.
  • C yanlış: Hata Python’dan geldiği için yorumlayıcının bozuk olduğu düşünülebilir. Ama yorumlayıcıyla ilgili bir sorun değildir.
  • E yanlış: Paket yapısını sadeleştirmek sorunu çözecekmiş gibi görünebilir. Ama klasörü paket olarak işaretleyen dosyayı silersiniz; sorun büyür.

19. Doğru: B. Demet döndürmek iç listeyi dışarı vermez; çağıran koleksiyonu doğrudan değiştiremez. İç listeyi olduğu gibi döndürmek 9. haftada gördüğümüz kapsülleme açığını yaratır: dış kod listeye append ile kitap ekleyip üç kitap sınırını atlayabilir.

  • A yanlış: Bu bölümde __eq__ de konuşulduğu için iki konu birbirine bağlanabilir. Ama eşitliğin dönüş türüyle ilgisi yoktur; listeler de == ile karşılaştırılabilir.
  • C yanlış: Demet değiştirilemez olduğu için kitap sayısının da sabit olduğunu anlatıyormuş gibi görünür. Ama kitap sayısı değişir; değişmeyen, dışarı verilen demetin kendisidir.
  • D yanlış: Demetin daha hafif bir yapı olduğu duyulmuş olabilir. Ama seçimin nedeni hız değil, dış kodun listeyi değiştirememesidir.
  • E yanlış: Demetin değiştirilemezliği içindekilere de yayılıyormuş gibi düşünülebilir. Ama demet içindeki Book nesneleri hâlâ değiştirilebilir; korunan koleksiyonun kendisidir.

20. Doğru: B. Üye ve kitap bulunur, üyenin koşulları ve kitabın uygunluğu kontrol edilir; ancak bundan sonra iki durum da değiştirilir. Birinci değişiklik yapılıp ikincisi başarısız olursa sistem yarım güncellenmiş kalır. “Önce doğrula, sonra değiştir” ilkesi burada iki nesneye birlikte uygulanır.

  • A yanlış: Program çökmediği için sorun çözülmüş gibi görünür. Ama hata gizlenir ve yarım durum kalıcı olur.
  • C yanlış: Her nesnenin kendi kuralını koruması iyi tasarım olduğu için yeterli gibi görünür. Ama her nesne kendi invariant’ını korusa da aralarındaki tutarlılık ayrı bir sorudur.
  • D yanlış: Sonuç değişiklikten sonra denetlendiği için güvence veriyormuş gibi görünür. Ama assert iş kuralını korumaz; üstelik kontrol değişiklikten sonra yapıldığı için geç kalır.
  • E yanlış: Hata çıkınca geri almak esnek bir çözüm gibi görünür. Ama geri alma kodu kolayca eksik kalır; yarım güncelleme riski sürer.

21. Doğru: A. in ve remove() elemanları == ile karşılaştırır; __eq__ tanımlı değilse varsayılan kimlik karşılaştırması kullanılır. Kendi sınıfımızda __eq__ yoksa iki ayrı örnek, ISBN’leri aynı olsa bile eşit sayılmaz.

  • B yanlış: remove() bir elemanı listeden çıkardığı için konumla çalıştığı sanılabilir. Ama in ve remove() bir değeri arar, konuma bakmaz; konumla silmek için pop() ya da del kullanılır.
  • C yanlış: __eq__ tanımlanmamış sınıflarda sonuç kimlik karşılaştırmasıyla aynı çıktığı için bu sanılabilir. Ama eşitlik tanımlıysa kimlik yeterli değildir; == kullanılır.
  • D yanlış: set ve dict aramaları hash kullandığı için listede de öyle olduğu sanılabilir. Ama hash yalnız bu yapılarda kullanılır; liste araması değer eşitliğine bakar.
  • E yanlış: Yazdırılınca aynı görünen iki nesnenin eşit sayılacağı düşünülebilir. Ama metin temsili karşılaştırmada kullanılmaz.

22. Doğru: E. in, list.remove(), liste karşılaştırmaları ve başka koleksiyon işlemleri de __eq__ metoduna başvurur. Member sınıfındaki in kontrolü ve remove() çağrısı, bu satırlara dokunulmadan farklı davranmaya başlar. Bu istenen davranış olabilir ama etkisi bilerek test edilmelidir.

  • A yanlış: Metot == işleciyle eşleştirildiği için yalnız orada çalıştığı sanılabilir. Ama koleksiyon işlemleri de aynı metoda başvurur.
  • B yanlış: is de bir karşılaştırma işleci olduğu için onun da etkileneceği düşünülebilir. Ama is her zaman kimliği sorar ve __eq__ tanımından etkilenmez.
  • C yanlış: Özel metotlar birbirine bağlıymış gibi görünebilir. Ama metin temsili ayrı bir metottur ve __eq__ onu değiştirmez.
  • D yanlış: Metottaki isinstance() kontrolü etkiyi sınırlıyormuş gibi görünür. Ama tür kontrolü olsun ya da olmasın, in ve remove() gibi koleksiyon işlemleri de bu metodu kullanır.

23. Doğru: D. Python -O seçeneğiyle çalıştırıldığında assert satırları kaldırılabilir; kural hata vermeden ortadan kalkar. İş kuralı programın normal işleyişinin parçasıdır ve if ...: raise ... ile korunmalıdır.

  • A yanlış: assert geliştirici kontrolü olarak anlatıldığı için hatasının yakalanamayacağı düşünülebilir. Ama AssertionError da yakalanabilir; sorun, satırın kaldırılabilmesidir.
  • B yanlış: assert koşul doğruysa geçtiği için yalnız bool değerlerle çalıştığı sanılabilir. Ama herhangi bir koşulu sınayabilir; amount > 0 de bir karşılaştırmadır.
  • C yanlış: assert bir fonksiyon gibi parantezle yazılmadığı için ikinci bir değer alamıyormuş gibi görünür. Ama virgülden sonra ikinci argüman olarak mesaj verilebilir.
  • E yanlış: assert test dosyalarında çok görüldüğü için yalnız oraya ait sanılabilir. Ama her yerde yazılabilir; sorun, -O ile kaldırılabilmesidir.

24. Doğru: E. Ödünç alınan kitapların listesini üye tutar; kural, sınırladığı listeyi tutan sınıfta durmalıdır. İşlemi yöneten Library de iki nesneyi değiştirmeden önce member.ensure_can_borrow(book) çağırır; böylece yarım güncelleme riski azalır.

  • A yanlış: Sabit sınıf gövdesinde yazıldığı için gerekçe bir biçim kuralı gibi görünür. Böyle genel bir kural yoktur; buradaki gerekçe, listenin sahibinin Member olmasıdır.
  • B yanlış: Library işlemin sırasını yönettiği için sabitlerle uğraşmaması gerektiği sanılabilir. Ama sabit tutabilir; belirleyici olan, kuralın hangi nesnenin durumunu sınırladığıdır.
  • C yanlış: Sınıf niteliğinin sonradan değiştirilebildiğini bilen öğrenci amacın bu olduğunu düşünebilir. Ama kararın amacı sınırı program çalışırken değiştirmek değildir.
  • D yanlış: Örnekte sabit sınıf içinde durduğu için başka yerde tanımlanamayacağı sanılabilir. Ama modül düzeyinde de tanımlanabilir.

25. Doğru: A. 1., 2. ve 3. kitabın alınabildiği, 4. kitabın reddedildiği, reddedilen kitabın hâlâ ödünç alınabilir durumda olduğu ve üyenin koleksiyonunda yalnız üç kitap bulunduğu doğrulanır. Sınırın hem altı hem üstü sınanır; ayrıca reddedilen işlemin yan etki bırakmadığı gösterilir.

  • B yanlış: Kural reddetmeyle ilgili olduğu için yalnız ret durumunu sınamak yeterli görünür. Ama sınırın altındaki davranış ve yan etkisizlik de doğrulanmalıdır.
  • C yanlış: Kullanıcının gördüğü şey mesaj olduğu için onu sınamak akla gelir. Ama mesaj metni ikinci planda kalır; bakılması gereken, kitabın ve üyenin durumunun tutarlı kalmasıdır.
  • D yanlış: Sınır koleksiyon uzunluğuyla ölçüldüğü için tek kontrol bu gibi görünür. Ama reddedilen kitabın durumunun bozulmadığı ayrıca kontrol edilmelidir.
  • E yanlış: Kural bu sabitle yazıldığı için sabiti sınamak kuralı sınamak gibi görünür. Ama sabitin değeri davranışı doğrulamaz.

Dönem özeti

  • Nesne tasarımında sınıfları yazmak işin bir kısmıdır. Hangi nesnenin durumunu kimin ve hangi sırayla değiştireceğine de siz karar verirsiniz.
  • Birden fazla nesneyi değiştiren işlemde nesneler arası tutarlılık ayrıca düşünülmelidir.
  • __eq__ tanımı in ve remove() gibi koleksiyon işlemlerini de etkiler.
  • is iki adın aynı nesneyi gösterip göstermediğini, == ise iki nesnenin eşit sayılıp sayılmadığını sorar.
  • assert geliştirirken ve test ederken kullanılır; python -O ile kaldırılabildiği için iş kuralını korumaz.
  • Kapsülleme, nesnenin içindeki değiştirilebilir (mutable) listeleri dışarıdan korumayı da gerektirir.
  • Dönem sonunda sizden beklenen, küçük bir değişikliği gerekçesini açıklayarak yapmanız, kodun çalıştığını göstermeniz ve kontrollerle doğrulamanızdır.

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

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

  • Bir sistemi okurken hangi sınıfın hangi durumu koruduğunu belirler.
  • Birden çok nesneyi değiştiren işlemde nesneler arası tutarlılığı gözetir.
  • in ve list.remove() işlemlerinin == eşitliğini kullandığını gösterir.
  • __eq__ eklemenin koleksiyon davranışını da değiştirdiğini fark eder ve etkiyi sınar.
  • İnvariant’ı raise ile, geliştirici kontrolünü assert ile yazar.
  • Bir iş kuralını, koruduğu durumun sahibi olan sınıfa koyar.
  • Bir tasarım değişikliğini regresyon kontrolleriyle doğrular.
Back to top