Özel metotlar ve özel istisnalar

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.

str ve len gibi Python işlemlerinin __str__ ve __len__ gibi özel metotlara bağlanmasını gösteren diyagram.

Özel metot protokolü

Bu hafta neleri yapabilmelisiniz?

  • __str__ ve __repr__ amaçlarını ve geri düşme davranışlarını ayırabilmeli,
  • Koleksiyonların eleman gösteriminde neden __repr__ kullandığını açıklayabilmeli,
  • __eq__ ile değer eşitliği tanımlayabilmeli,
  • NotImplemented ile NotImplementedError arasındaki farkı açıklayabilmeli,
  • Eşitlik tanımının hash edilebilirlik üzerindeki etkisini tanıyabilmeli,
  • Özel istisna sınıfı tanımlayıp uygun temel istisna sınıfını seçebilmelisiniz.

__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:

  1. Sınıfta __str__ yok ama __repr__ varsa, str(obj)/print(obj) geliştirici temsilini kullanabilir.
  2. Sadece __str__ tanımlamak repr(obj) davranışını değiştirmez; repr kendi varsayılan temsilinde kalır.
  3. 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
NotImplementedError istisna sınıfı raise edilir bir davranışın burada uygulanmadığını bildirmek

Doğru örnekler:

def __eq__(self, other):
    if not isinstance(other, Product):
        return NotImplemented
class Shape:
    def area(self):
        raise NotImplementedError("Alt sınıf hesaplamalı")
Warningreturn NotImplementedError ve raise NotImplemented yazmayın
  1. haftada NotImplementedError fırlattık; bu hafta __eq__ içinde NotImplemented dö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.

Genel bir hata yerine alanı ifade eden özel istisna türünün yükseltilmesi ve yakalanmasını gösteren diyagram.

Özel istisna

Alıştırma — __str__ ekle

return f"{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.

Kendinizi kontrol edin

  1. __str__ ile __repr__ hangi hedeflere yönelir?
  2. Liste elemanlarını gösterirken neden __repr__ önemlidir?
  3. Sadece __repr__ varsa print(obj) ne yapabilir?
  4. NotImplemented ile NotImplementedError arasındaki fark nedir?
  5. __eq__ neden problem alanı kararıdır?
  6. Özel istisnalar neden genellikle Exception veya anlamlı bir alt sınıftan türetilir?
  7. ValueError alt sınıfı yapmak hangi durumda anlamlı olabilir?

Bu haftadan akılda kalması gerekenler

  • Ö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.
Back to top