Son haftada yeni bir OOP kavramı yok. Dönem boyunca öğrendiklerinizi tek bir küçük kütüphane programında 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 bir değişikliğin başka neleri etkilediğini göstermek.
Bu sayfadaki kod hücreleri kendiliğinden çalışmaz. Her hücrede önce çıktıyı tahmin edin, sonra Run Code düğmesine basın. Kodu değiştirip yeniden çalıştırabilir, Start Over ile ilk hâline döndürebilirsiniz.
Bu bölümün kapsamı
NoteSınavda sorulur
Bir sistemi okurken hangi sınıfın hangi durumu koruduğunu belirleme
Bir iş kuralının hangi sınıfa ait olduğuna, kuralın koruduğu durumun sahibine bakarak karar verme
İnvariant için raise, geliştirici kontrolü için assert
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
Tasarım değişikliğini regresyon kontrolleriyle doğrulama
NoteSınav dışı (bilgi amaçlı)
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 ya da unittest ile otomatik test yazımı
Bu başlıklar konunun devamıdır. İleride karşınıza çıkar, ama bu derste ezberlemeniz beklenmiyor.
Kütüphane programındaki beş işi hangi sınıfa yazardınız?
Küçük bir kütüphane programı yazıyorsunuz. Programda üç sınıf var: Book bir kitabın ISBN’ini ve adını, Member bir üyenin numarasını ve adını tutuyor. Library kütüphanedeki bütün kitapları ve üyeleri bir arada tutuyor. Kütüphaneden şu beş istek geldi:
Her kitabın kimde olduğunu ve her üyenin hangi kitapları aldığını kaydetmek
Bir üyeye aynı anda en fazla üç kitap vermek, bu sınırın programın başka bir yerinden aşılmasına izin vermemek
Ödünç verirken kitabın kaydını ve üyenin listesini birlikte güncellemek: kitap ödünçte görünüp üyenin listesinde eksik kalmamalı
Kitabı yalnız onu ödünç alan üyeden geri almak
Aynı ISBN’li iki Book nesnesini aynı kitap saymak
Dönem boyunca sınıf, nitelik, property, raise, nesne koleksiyonları ve __eq__ gördünüz. Bu beş işin her birini hangi sınıfa, neyle yazardınız? Okumaya devam etmeden önce her iş için bir satır yazın. Kod yazmanız gerekmiyor: “Library’ye bir sözlük eklerim” de bir cevap.
Bölümde başlangıç kodunu okuyup beş işin nereye yazıldığına tek tek bakacağız. Bölümün sonunda listenize döneceğiz.
Başlangıç sistemi: kim neyi tutuyor?
Aşağıdaki kod çalışan bir sistem. Üç sınıfı tanımlıyor, en altta da bir kitabı Ada’ya ödünç veriyor. Kodu baştan sona okuyun. Son iki satır ne yazar?
Hücreyi çalıştırdıktan sonra kodda şu soruların cevabını arayın:
Bir kitabın kimde olduğunu hangi sınıf, hangi nitelikte tutuyor?
Bir üyenin aldığı kitapları hangi sınıf tutuyor? Bu listeyi dışarıya olduğu gibi veriyor mu?
Library kitapları ve üyeleri neden sözlükte tutuyor?
Ödünç verilen Book nesnesi hem library.books sözlüğünde hem Ada’nın listesinde duruyor. Bunlar iki ayrı kitap mı?
TipCevaplar
Book, _borrowed_by niteliğinde. Kitap kütüphanedeyse bu nitelik None, ödünçteyse kitabı alan Member nesnesini gösterir. Dışarıdan yalnız borrowed_by ve is_available property’leriyle okunur.
Member, _borrowed_books listesinde. Dışarıya listenin kendisini değil, borrowed_books property’siyle bir demet (tuple) kopyasını verir.
Kitabı ISBN’iyle, üyeyi numarasıyla aramadan bulmak için. Sözlüğün anahtarı tek olduğu için aynı ISBN ya da aynı üye numarası iki kez kaydedilemez.
Hayır. Hücre True yazdı: iki koleksiyon da aynı Book nesnesini gösteriyor. Koleksiyona eklemek nesneyi kopyalamaz, nesneye bir referans daha ekler.
Birinci iş, kitabın kimde olduğunu kaydetmekti. Bu kayıt iki yerde duruyor: kitap kendisini alan üyeyi, üye de aldığı kitapları biliyor. Bölümün geri kalanında bu iki kaydın her zaman birbiriyle uyumlu kalmasını sağlayacağız.
Üye başına en fazla üç kitap
Programın uyması gereken “üye başına en fazla üç kitap” gibi kurallara iş kuralı denir. Bir iş kuralını hangi sınıfa koyacağınıza karar verirken kuralın hangi durumu sınırladığına bakın. Üç kitap sınırı üyenin aldığı kitapları sayar. O listeyi Member tutuyor, kural da orada duruyor: MAX_BORROWED_BOOKS sınıf niteliği ve ensure_can_borrow() metodu.
Aşağıdaki hücrede Member sınıfı tek başına duruyor. Kitapların yerinde kısaca adları var, çünkü Member kitaba bakmıyor, yalnız sayıyor. Ada üç kitap aldıktan sonra dördüncüyü istiyor. Hücre ne yazar? Son satır sınırı aşabilir mi?
TipÇıktı satır satır
Reddedildi: Ödünç kitap sınırı dolu: dördüncü add_book() çağrısında ensure_can_borrow() hata yükseltti, kitap listeye eklenmedi.
3: listede hâlâ üç kitap var.
AttributeError: borrowed_books bir demet döndürüyor. Demette append() metodu yoktur.
borrowed_books iç listeyi değil, onun demet kopyasını verir. Dışarıdaki kod bu demete eleman ekleyemez, iç listeye de ulaşamaz. 9. haftada gördüğünüz gibi, property iç listeyi olduğu gibi döndürseydi dış kod append() ile dördüncü kitabı ekleyebilir, ensure_can_borrow() kontrolüne hiç uğramazdı.
Bu kuralın çalıştığını şu kontrollerle gösterirsiniz:
1., 2. ve 3. kitap ödünç alınır.
4. kitap reddedilir.
Reddedilen kitap hâlâ ödünç alınabilir durumdadır.
Üyenin listesinde yalnız üç kitap vardır.
Sınırı neden assert ile yazmıyoruz?
Bir noktada neyin doğru olmasını beklediğinizi koda şöyle satırlarla yazabilirsiniz:
assert book.borrowed_by is memberassertlen(member.borrowed_books) ==3
assert, koşul yanlışsa AssertionError yükseltir, doğruysa hiçbir şey yapmaz. Yukarıdaki kontrolleri böyle yazmak, test yazmaya doğru atılmış iyi bir ilk adımdır.
Sınırın kendisini de add_book() içinde assert ile yazmak akla gelebilir:
Bu güvenli değildir. Python -O (optimize) seçeneğiyle çalıştırıldığında assert satırları kaldırılabilir ve kural da onlarla birlikte ortadan kalkar. Program gerçek kullanıcılara hizmet verirken (üretimde) böyle çalıştırılabilir.
Importantİnvariant için raise, geliştirici kontrolü için assert
Değişmez koşul (invariant), nesne kullanıldığı sürece doğru kalması gereken kuraldır: “üyede en fazla üç kitap olur” gibi. Kullanıcı girdisini, bir iş kuralını ya da nesnenin invariant’ını koruyan kontrol programın her çalışmasında yapılmalıdır. Bunun için if ...: raise ... kullanın. assert ise geliştirirken ya da test ederken “burada doğru olduğunu varsaydığım şey gerçekten doğru mu?” diye sorduğunuz kontroller içindir.
Ödünç alma: iki kayıt birlikte değişir
Her nesnenin kendi kuralını koruması yetmeyebilir. Ödünç alma hem Book hem Member nesnesini değiştirir. Aşağıdaki hücrede borrow() fonksiyonu önce kitabı işaretliyor, sonra üyenin listesine ekliyor. Sınır kontrolü Member içinde. Ada üç kitap aldıktan sonra dördüncüyü istiyor. Son iki satır ne yazar?
İşlem reddedildi, ama kitap çoktan Ada’yı gösteriyor. Ada’nın listesinde ise yok. Member kendi kuralını korudu ve dördüncü kitabı almadı. Yine de iki kayıt birbiriyle çelişiyor: sistem yarım güncellenmiş kaldı.
ImportantBir sınıfın invariant’ı yetmeyebilir
Bir işlem birden fazla nesneyi birlikte değiştiriyorsa bu nesnelerin birbiriyle uyumlu kalmasını ayrıca düşünmeniz gerekir. Buna nesneler arası tutarlılık diyoruz.
Başlangıç sistemindeki Library.borrow() bu yüzden 8. haftadaki “önce kontrol et, sonra değiştir” sırasını iki nesneye birlikte uygular:
# Önce bütün ön koşullar, sonra durum değişiklikleri.member.ensure_can_borrow(book)ifnot book.is_available:raiseValueError("Kitap zaten ödünçte")book.borrow_by(member)member.add_book(book)
Üye ve kitap bulunur.
Üyenin ödünç alma koşulları kontrol edilir.
Kitabın rafta olup olmadığı kontrol edilir.
Ancak bundan sonra iki nesne de değiştirilir.
Kontrollerden biri hata verirse iki nesneye de dokunulmamış olur. Library kuralı kendisi yazmıyor: sınırı Member’a, rafta olup olmadığını Book’a soruyor. Yalnız sorma sırasını o belirliyor.
NoteBaş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ıyor. Bu derste kontrolleri değişikliklerden önce yapmanız yeterli.
İade: kitap yalnız onu alan üyeden döner
İade de iki nesneyi birlikte değiştirir: kitap rafa döner, üyenin listesinden çıkar. Başlangıç sistemindeki Library.return_book() metoduna bakmadan önce iadenin hangi durumlarda reddedilmesi gerektiğini 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.
Son durum, önceki hücrede gördüğünüz yarım güncellemedir. Şimdi return_book() metodunu okuyun. Hangi satır hangi durumu yakalıyor?
if member isNone:raiseValueError("Üye bulunamadı")if book isNone:raiseValueError("Kitap bulunamadı")if book.borrowed_by isnot member:raiseValueError("Kitap bu üyede değil")if book notin member.borrowed_books:raiseValueError("Kitap ve üye kaydı tutarlı değil")book.return_book()member.remove_book(book)
book.borrowed_by is not member satırı iki durumu birden yakalar: kitap kütüphanedeyse borrowed_byNone’dır, başka üyedeyse başka bir Member nesnesini gösterir. Burada == değil is kullanılıyor, çünkü soru “kitabı alan bu üye mi?” sorusudur.
Başarılı bir iadeden sonra şu iki koşul doğru olmalı:
assert book.is_availableassert book notin member.borrowed_books
Bu assert satırları test kontrolüdür. return_book() içindeki raise satırlarının yerini tutmaz.
ISBN’e göre eşitlik
Beşinci iş için Book sınıfına 12. haftadaki gibi ISBN’leri karşılaştıran bir __eq__ ekleyeceğiz:
class Book: ...def__eq__(self, other):ifnotisinstance(other, Book):returnNotImplementedreturnself.isbn == other.isbn
Yalnız Book sınıfını değiştiriyoruz. Ama Member sınıfında şu iki satır var:
if book inself._borrowed_books: ...self._borrowed_books.remove(book)
in ve remove() hangi eşitliği kullanıyor?
in listede eleman ararken, list.remove() de silinecek elemanı bulurken elemanları == ile karşılaştırır. Başlangıç sisteminde Book sınıfında __eq__ yok. O zaman == iki nesnenin kimliğine bakar: ISBN’leri aynı olsa bile iki ayrı Book örneği eşit sayılmaz.
Aşağıdaki hücrede Book sınıfı __eq__ ile birlikte kısaltılmış hâlde duruyor. Listede yalnız registered var. probe aynı ISBN’li başka bir nesne. Üç print() ne yazar? books.remove(probe) hata verir mi?
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. Şimdi __eq__ metodunu hücreden silin ve yeniden çalıştırın. Ne değişti?
TipÇıktı satır satır
__eq__ varken:
False: registered ile probe iki ayrı nesne.
True: in, probe’u listedeki registered ile == üzerinden eşleştirdi.
[]: remove() eşit sayılan registered nesnesini sildi.
__eq__ silinince ilk satır yine False, ikinci satır False olur. remove(probe) ise ValueError yükseltir, çünkü == artık kimliğe bakıyor ve listede probe yok.
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. 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.
Kütüphane modeliniz için istediğiniz davranış bu olabilir. Ama bu etkiyi bilerek sınamanız gerekir.
Değişiklikten sonra eski davranışı sınamak
Bir değişiklikten sonra eskiden çalışan davranışın bozulup bozulmadığını ya da nasıl değiştiğini sınayan kontrole regresyon kontrolü denir. __eq__ ekledikten sonra şunları gösterin:
Aynı ISBN’li iki farklı nesne == ile eşit, is ile farklıdır.
Aynı ISBN’li ikinci nesne, in kontrolünde ilk nesneyle eşleşir.
remove() artık ISBN eşitliğine göre siler.
Üç kitap sınırı ve iade kontrolleri hâlâ geçer.
Bir şeye daha dikkat edin: 12. haftada gördüğünüz gibi, __eq__ yazıp __hash__ yazmadığınız sınıfın nesneleri artık bir kümeye eleman ya da sözlüğe anahtar olamaz. Bu modelde sözlüğün anahtarı kitabın kendisi değil ISBN metni olduğu için Library etkilenmez.
Son olarak bu davranışı modelinizde isteyip istemediğinizi gerekçesiyle kısaca yazın. Bu iş için 6. haftanın kimlik (is), 9. haftanın nesne koleksiyonları ve 12. haftanın eşitlik (==, __eq__) konularını birlikte kullanırsınız.
Soruya dönelim: beş iş hangi sınıfta?
Bölümün başında kütüphane programındaki beş işi hangi sınıfa, neyle yazacağınızı sormuştuk. Başlangıç sistemindeki karşılıkları şöyle:
İş
Önceki haftalarda
Bu bölümdeki yol
Dikkat
Kitabın kimde olduğunu kaydetmek
Nesne başka nesneyi tutar, koleksiyon (9. hafta)
Book._borrowed_by üyeyi, Member._borrowed_books kitapları, Library iki sözlükte bütün kayıtları tutar
Aynı Book nesnesi iki koleksiyonda durur, iki ayrı kitap olmaz
Üye başına en fazla üç kitap
Sınıf niteliği (7. hafta), raise ile kural, property (8. hafta)
Member.MAX_BORROWED_BOOKS ve ensure_can_borrow(), borrowed_books demet döndürür
Kuralı assert ile yazmayın: -O ile kaldırılabilir
İki kaydı birlikte güncellemek
Önce kontrol et, sonra değiştir (8. hafta)
Library.borrow() bütün kontrollerden sonra iki nesneyi değiştirir
Her nesnenin kendi kuralı yarım güncellemeyi önlemez
Yalnız alan üyeden geri almak
Kimlik ve is (6. hafta)
Library.return_book()book.borrowed_by is member diye sorar
Kayıtların çelişmesini de ayrıca kontrol eder
Aynı ISBN’li kitapları eşit saymak
__eq__ (12. hafta)
Book.__eq__ ISBN’leri karşılaştırır
in ve remove() da bu tanımı kullanır, regresyon kontrolü yazın
Listenizi tabloyla karşılaştırın. Üç kitap sınırını Library’ye yazdıysanız kod çalışırdı, ama sınırı aşmanın başka bir yolu kalırdı: üyenin listesine Library’ye uğramadan ekleme yapan her kod kuralı atlardı. Kural, sınırladığı listeyi tutan sınıfta durunca bu yol kapanır. İki kaydı birlikte güncellemek için de yeni bir araç gerekmedi. 8. haftada tek nesnede kurduğunuz “önce kontrol et, sonra değiştir” sırasını Library iki nesneye birlikte uyguluyor.
Bütünleştirici görev
Bu görevi, alttaki tasarım savunmasını ve deneme sınavını evde, kendi bilgisayarınızda yapın. Teslim edilmez, not getirmez. Başlangıç sistemi ilk dört işi zaten yapıyor. Sizin işiniz beşinciyi eklemek ve hepsini kontrollerle göstermek. Bölümün başındaki hücredeki kodu bir dosyaya kopyalayın ve şunları yapın:
Book.__eq__’yi ISBN’e göre tanımlayın.
Üye başına en fazla üç kitap kuralını kontrollerle gösterin.
İadenin yanlış üyeden reddedildiğini gösterin.
Member’ın iç listesinin dışarıdan değiştirilemediğini gösterin.
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ı assert ile 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 ve in davranışı
İş kurallarını raise ile koruyun. assert satırlarını yalnız sonucu doğrulamak için kullanın. Hata beklediğiniz senaryoda çağrıyı try/except içine alın ve hatanın geldiğini kontrol edin.
Kısa tasarım savunması
Kodun altına şu sorular için en fazla 150 kelimelik bir cevap yazın:
Ödünç alma sınırı neden Member sınıfının işidir?
Library ön koşulları neden yine de değişiklikten önce sırayla kontrol eder?
Book.__eq__ eklemek koleksiyon davranışını nasıl değiştirdi?
Member’ın iç listesi neden dışarıya değiştirilebilir (mutable) bir liste olarak verilmedi?
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
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
TipCevaplar ve gerekçeler
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.
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ış: Nesnenin alanlarını gösterdiği için akla yatkın görünür. Ama bu bir __repr__ çıktısı biçimidir ve sınıfta __repr__ tanımlı değildir.
C yanlış: C’de bir struct doğrudan printf’e verilemediği için Python’da da hata beklenebilir. Ama print() her nesneyi alır ve onu metne çevirerek yazar.
D yanlış: Nesneyi yazdırınca bu çıktıyı daha önce gördüyseniz yine bunu beklersiniz. Ama varsayılan temsil yalnız sınıfta __str__ de __repr__ de yoksa görünür. Burada __str__ var.
E yanlış:__str__ içinde print çağrılmadığı için ekrana bir şey çıkmayacağı sanılabilir. Ama __str__ metni döndürür, print de o metni 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’ın 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 ve 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 ve 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.
Bir iş kuralı, sınırladığı durumu tutan sınıfta durur. Üç kitap sınırı üyenin listesini sınırladığı için Member’dadır.
Kapsülleme, nesnenin içindeki değiştirilebilir (mutable) listeleri dışarıdan korumayı da gerektirir. Property iç listeyi değil, demet kopyasını döndürür.
Birden fazla nesneyi değiştiren işlemde nesneler arası tutarlılığı ayrıca düşünün: önce bütün kontroller, sonra bütün değişiklikler.
is iki adın aynı nesneyi gösterip göstermediğini, == ise iki nesnenin eşit sayılıp sayılmadığını sorar.
__eq__ tanımı in ve remove() gibi koleksiyon işlemlerini de etkiler. Bu etkiyi regresyon kontrolleriyle sınayın.
İş kuralını if ...: raise ... korur. assert geliştirirken ve test ederken kullanılır, python -O ile kaldırılabildiği için iş kuralını korumaz.
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.