Python’daki nesneler yalnızca bizim yazdığımız normal metotlarla değil, dilin veri modeliyle de etkileşir. print(obj), repr(obj), obj1 == obj2 veya bir koleksiyon içinde nesnenin görünümü gibi davranışlar özel metotlarla tanımlanabilir. Bu hafta ayrıca problem alanına özgü hataları kendi istisna sınıflarımızla ifade edeceğiz.
Özel metot protokolü
Bu bölümün kapsamı
NoteSınavda sorulur
__str__ ile kullanıcıya dönük temsil
__repr__ ile durumu açık gösteren temsil
koleksiyonların elemanlar için repr() kullanması
__eq__ ile alan kuralına göre eşitlik
NotImplemented ile NotImplementedError rol farkı
__eq__ tanımlayan sınıfın hash sözleşmesi
özel istisna için uygun temel sınıf seçimi
NoteSınav dışı (bilgi amaçlı)
__hash__ yazımı
__lt__, __add__ gibi diğer özel metotlar ve functools.total_ordering
istisna zincirleme (raise ... from ...)
Bu başlıklar konunun devamıdır; ileride karşınıza çıkar ama bu derste ezberlemeniz beklenmiyor.
Bu iki nesne ne zaman “aynı” sayılır?
Aynı ISBN’li iki Book nesnesi aynı kitap mıdır? Cevap Python’da değil, sizin problem alanınızdadır. Kararı verdikten sonra == dışında nelerin değiştiğini de göreceğiz; in ve remove() aynı tanımı kullanır.
__str__: kullanıcıya dönük temsil
print(product) ve str(product) mümkün olduğunda __str__() sonucunu kullanır.
__repr__: geliştiriciye dönük temsil
Bu derste öncelik, repr çıktısının hata ayıklamada nesnenin durumunu açık göstermesidir.
__str__ / __repr__ geri düşme davranışı
Üç pratik kural önemlidir:
Sınıfta __str__ yok ama __repr__ varsa, str(obj)/print(obj) geliştirici temsilini kullanabilir.
Sadece __str__ tanımlamak repr(obj) davranışını değiştirmez; repr kendi varsayılan temsilinde kalır.
Liste gibi koleksiyonlar elemanlarını gösterirken repr() kullanır. Bu yüzden print([product]), product.__str__() yerine product.__repr__() sonucunu gösterir.
Importantprint(obj) ile print([obj]) aynı protokolü kullanmaz
print(obj) kullanıcıya dönük str temsilini tercih eder. Bir listenin kendi repr çıktısı ise elemanların repr temsilini kullanır. __str__ yazdığınız hâlde print(products) çıktısının değişmemesi çoğu zaman bu yüzdendir.
__eq__: iki nesne ne zaman eşit?
== alan kuralımıza göre eşitliği; is hâlâ kimliği sorar.
NoteEşitlik tasarım kararıdır
Product için kod, Book için ISBN veya Student için öğrenci numarası “aynı kabul edilme” kuralı olabilir. __eq__ yazmadan önce bu kuralı problem alanında belirleyin.
NotImplemented ve NotImplementedError aynı şey değildir
İsimleri benzese de tamamen farklı rolleri vardır:
Ad
Tür
Ne yapılır?
Tipik kullanım
NotImplemented
özel singleton değer
return edilir
__eq__ gibi ikili işlemde bu türle karşılaştırmayı desteklemiyorum
class Shape:def area(self):raiseNotImplementedError("Alt sınıf hesaplamalı")
Warningreturn NotImplementedError ve raise NotImplemented yazmayın
haftada NotImplementedErrorfırlattık; bu hafta __eq__ içinde NotImplementeddöndürüyoruz. Bu ayrımı özellikle koruyun.
__eq__ ve hash sözleşmesi
Eşit kabul edilen nesnelerin aynı hash değerine sahip olması gerekir. Bir sınıf __eq__ tanımlayıp uygun __hash__ tanımlamazsa örnekleri çoğu durumda hash edilemez hâle gelir. Bu derste __hash__ yazmıyoruz; mutable alan nesnelerini set elemanı veya dict anahtarı yapmadan önce eşitlik/hash tasarımını düşünmeniz yeterlidir.
Özel istisna hangi sınıftan türemeli?
Genel alan hataları için güvenli başlangıç:
class InsufficientBalanceError(Exception):pass
Uygulama düzeyindeki özel istisnaları doğrudan BaseException sınıfından türetmeyin; BaseException, KeyboardInterrupt ve SystemExit gibi program kontrolüyle ilgili istisnaların da köküdür.
Bazen özel hatanız aynı zamanda yerleşik bir hata kategorisinin özel biçimiyse daha anlamlı bir temel seçebilirsiniz:
class InvalidScoreError(ValueError):pass
Bu durumda çağıran kod ister InvalidScoreError, ister daha genel ValueError olarak yakalayabilir. Hangi tabanın doğru olduğu hata semantiğine bağlıdır; sırf özel sınıf yazmak için gereksiz hiyerarşi kurmayın.
Alan hatalarını açık ifade etmek
Nesne metodu çoğu zaman hatayı üretir, uygulama/UI katmanı hatayı yakalar. Alan nesnesi hatayı kendi içinde print() edip yutarsa çağıran kod başarısızlığı güvenilir biçimde ayırt edemez.
Özel istisna
Alıştırma — __str__ ekle
returnf"{self.student_id} — {self.name}"
Alıştırma — repr farkını gözlemle
Bir sınıfa hem __str__ hem __repr__ ekleyin. print(obj), repr(obj) ve print([obj]) sonuçlarını yan yana yazdırın; hangi çağrının hangi özel metoda gittiğini açıklayın.
Alıştırma — Eşitlik kuralını seç
Book(isbn, title, author) sınıfında eşitliği ISBN üzerinden tanımlayın. Başka türden nesne geldiğinde NotImplemented döndürün. Bu alıştırmada __hash__ yazmanız beklenmez.
Küçük üretim görevi — Üyelik modeli
Member(member_id, name, active) sınıfı oluşturun. __str__, __repr__, __eq__ tanımlayın; eşitlik member_id üzerinden olsun. InactiveMemberError(Exception) özel istisnasını ve ensure_active() davranışını ekleyin.
Çalışır kod teslimi: İki farklı ama aynı member_id değerine sahip nesne için == ve is sonuçlarını doğrulayın; print(member) ve print([member]) farkını gösterin; pasif üyede özel istisnanın üretildiğini test edin. Ayrıca NotImplemented ile NotImplementedError için birer doğru kullanım satırı ekleyin.
Sınav provası
Önce kendi cevabını seç; sonra cevap anahtarında her şıkkın neden doğru veya yanlış olduğunu oku. Çeldiriciler uydurma değil, bu konuda gerçekten yapılan hatalardır. Sınav maddeleri de bu mantıkla yazılır.
C. Çağıran kod ister InvalidScoreError ister daha genel ValueError olarak yakalayabilir
D. Python hatayı kendiliğinden yakalar
E. İstisna daha hızlı yükseltilir
TipCevaplar ve gerekçeler
Madde 1 — Doğru: C.print(obj) ve str(obj) mümkün olduğunda __str__() sonucunu kullanır.
A yanlış: Bu bir __repr__ çıktısı biçimidir; burada __repr__ tanımlı değildir.
B yanlış:print() her nesneyi kabul eder ve metin temsilini ister.
D yanlış: Varsayılan temsil ancak __str__ ve __repr__ tanımlı değilse görünür.
E yanlış:__str__ bir metin döndürür ve print onu yazar.
Madde 2 — Doğru: D. Düşme yalnız tek yönlüdür: __repr__ varsa str ondan yararlanabilir, tersi geçerli değildir.
A yanlış:repr kendi varsayılanında kalır.
B yanlış: Varsayılan __repr__ her sınıfta bulunur.
C yanlış: Varsayılan temsil sınıf adı ve kimlik bilgisini içerir.
E yanlış:repr() her zaman bir metin döndürür.
Madde 3 — Doğru: E.print(obj) kullanıcıya dönük str temsilini tercih eder. Listenin kendi repr çıktısı ise elemanların repr temsilini kullanır.
A yanlış: İlk satırda __str__ tanımlı olduğu için o kullanılır.
B yanlış: Koleksiyonlar elemanları için str değil repr kullanır.
C yanlış: Liste gösterimi farklı protokole başvurur.
D yanlış: Sınıfta __repr__ tanımlıdır; varsayılan temsil kullanılmaz.
Madde 4 — Doğru: A.print(obj) ile print([obj]) aynı protokolü kullanmaz. Çözüm __repr__ da tanımlamaktır.
B yanlış: Biçimlendirir; ama eleman temsili için repr kullanır.
C yanlış: Doğru ad __str__tir.
D yanlış: Gerekli değildir; elemanın __repr__ metodu yeterlidir.
E yanlış: Eleman sayısıyla ilgisi yoktur.
Madde 5 — Doğru: B.== artık alan kuralımıza (ürün kodu) göre karar verir; is hâlâ kimliği sorar ve iki ayrı nesne vardır.
A yanlış:__eq__ yazmak nesneleri aynı nesne yapmaz.
C yanlış:__eq__ tanımlandığı için kodlar eşit olduğunda True döner.
D yanlış: Sonuçlar ters yazılmış.
E yanlış:other bir Product olduğu için o dal hiç çalışmaz.
Madde 6 — Doğru: C.Product için kod, Book için ISBN, Student için öğrenci numarası olabilir. Eşitlik bir tasarım kararıdır.
A yanlış: Bellek yerleşimiyle ilgisi yoktur.
B yanlış: Kalıtım eşitlik kuralını belirlemez.
D yanlış: Bu bir uygulama ayrıntısıdır; önce alan kuralı gelir.
E yanlış: Nitelik sayısı eşitlik kuralını belirlemez.
Madde 7 — Doğru: D. Biri “bu türle karşılaştırmayı desteklemiyorum” der; diğeri “bu davranış burada uygulanmadı” der.
A yanlış: Kullanımlar ters yazılmış; bu en sık yapılan hatadır.
B yanlış:NotImplemented bir istisna sınıfı değil, tekil bir değerdir.
C yanlış: İkili işlem metotlarında kullanılır; ABC ile ilgisi yoktur.
E yanlış:NotImplementedError bir istisna sınıfıdır.
Madde 8 — Doğru: E. Davranışın burada uygulanmadığını bildiren istisna yükseltilir.
A yanlış:NotImplemented bir istisna değildir; yükseltilemez.
B yanlış: Sessizce None döndürür ve eksik uygulama fark edilmez.
C yanlış: Bu, __eq__ gibi ikili işlemlerin kullanımıdır.
D yanlış: İstisna sınıfı döndürülmez; yükseltilir.
Madde 9 — Doğru: A. Eşit kabul edilen nesnelerin aynı hash değerine sahip olması gerekir. Python bu sözleşmeyi koruyabilmek için varsayılan hash’i kaldırır.
B yanlış: Otomatik üretim yapılmaz.
C yanlış: Örnek üretimi etkilenmez.
D yanlış: Etkilenir; varsayılan __hash__ kaldırılır.
E yanlış:== çalışır; kaybolan hash davranışıdır.
Madde 10 — Doğru: B.class InsufficientBalanceError(Exception): pass yaygın ve güvenli kalıptır.
A yanlış: İstisna olarak yükseltilemez.
C yanlış: Eksik uygulamayı bildirir; alan hatası kategorisi değildir.
D yanlış: Programı sonlandırma isteğini temsil eder.
E yanlış:BaseException, KeyboardInterrupt ve SystemExit gibi program kontrolü istisnalarının da köküdür; uygulama hataları buradan türetilmez.
Madde 11 — Doğru: C. Hata zaten bir değer geçersizliği ise bu taban semantiği doğru yansıtır. Yine de sırf özel sınıf yazmak için gereksiz hiyerarşi kurulmamalıdır.
A yanlış: Mesaj yükseltilirken verilir.
B yanlış: Günlükleme ayrı bir konudur.
D yanlış: Yakalama yine çağıranın işidir.
E yanlış: Başarım farkı ölçüt değildir.
Tek sayfa özet
Özel metotlar sınıfımızı Python’ın veri modeliyle uyumlu hâle getirir.
__str__ kullanıcı, __repr__ geliştirici odaklı temsildir; koleksiyonlar elemanlarda repr kullanır.
NotImplemented döndürülen özel bir değerdir; NotImplementedError fırlatılan bir istisnadır.
__eq__ eşitliğin alan anlamını kodlar; hash edilebilirlik ayrı ama ilişkili bir sözleşmedir.
Özel istisnalar çoğunlukla Exception veya semantik olarak daha uygun yerleşik bir istisna sınıfından türetilir.
Bu bölümün kazanımları
Bu bölümü bitiren öğrenci:
__str__ ile kullanıcıya dönük temsil yazar.
__repr__ ile hata ayıklamada nesnenin durumunu açık gösteren temsil yazar.
Koleksiyonların elemanları için repr() kullandığını belirler.
__eq__ ile alan kuralına göre eşitlik tanımlar ve kimlikten ayırır.
NotImplemented ile NotImplementedError arasındaki rol farkını gösterir.
__eq__ tanımlayan sınıfın hash sözleşmesini gözetmesi gerektiğini bilir.
Özel istisna sınıfı için uygun temel sınıfı seçer.