Polimorfizm ve duck typing
Polimorfizm, farklı nesnelerin ortak bir davranış üzerinden aynı akışta kullanılabilmesidir. Python’da bunun için her zaman ortak bir üst sınıf kurmak gerekmez. Bir nesne gerekli davranışı sunuyorsa onu kullanabiliriz. Bu yaklaşım yaygın olarak duck typing olarak adlandırılır.
Bu hafta neleri yapabilmelisiniz?
- polimorfizmi “aynı mesaj, farklı davranış” fikriyle açıklayabilmeli,
- farklı sınıflardan nesneleri ortak metot üzerinden kullanabilmeli,
- duck typing yaklaşımını örnekleyebilmeli,
- gereksiz
type()/isinstance()koşul zincirlerini fark edebilmeli, - kalıtım ile polimorfizmin aynı şey olmadığını açıklayabilmeli,
- davranış sözleşmesinin önemini tartışabilmelisiniz.
Aynı çağrı, farklı davranış
Geçen hafta iki bildirim türü oluşturmuştuk:
Döngü notification nesnesinin tam türünü bilmeden send() çağırır. Her nesne davranışı kendi biçiminde gerçekleştirir.

send() çağrısının e-posta, SMS ve push bildirim nesnelerinde farklı davranış üretmesi.Kalıtım olmadan da polimorfizm
Yukarıdaki iki sınıf ortak bir üst sınıftan türemedi. Yine de ikisi de send(message) davranışını sunduğu için aynı akışta kullanılabildi.
Python’da bu düşünce sık görülür:
Nesnenin kim olduğundan önce, gerekli davranışı sunup sunmadığına bak.
Bu yaklaşım duck typing olarak bilinir.
“Ördek gibi yürüyüp ördek gibi ses çıkarıyorsa ördek gibi kullanılabilir” benzetmesi terimin kökenidir. Teknik olarak önemli olan isim değil, programın bir nesnenin sınıf etiketi yerine sunduğu davranışa güvenebilmesidir.
Kötü sinyal: büyüyen tür zinciri
Şu kodu düşünün:
def send_notification(notification, message):
if type(notification) is EmailNotification:
return notification.send_email(message)
elif type(notification) is SmsNotification:
return notification.send_sms(message)Yeni bir bildirim türü geldiğinde bu fonksiyonu da değiştirmemiz gerekir.
Bunun yerine sınıflar ortak davranış sunabilir:
def send_notification(notification, message):
return notification.send(message)Yeni sınıf send() davranışını sağladığı sürece fonksiyon değişmez.
isinstance() kullanmak başlı başına hata değildir
Tür kontrolü bazı sınır doğrulamalarında, serileştirme kodunda veya gerçekten türe bağlı bir karar gerektiğinde anlamlı olabilir. Buradaki sorun type() ya da isinstance() fonksiyonlarının varlığı değil; nesnenin kendi davranışını çağırmak yerine her yeni tür için büyüyen bir koşul zinciriyle davranış seçmeye çalışmaktır.
Davranış sözleşmesi
Duck typing “her şeyi dene, çalışırsa olur” anlamına gelmez. Ortak davranışın anlamı açık olmalıdır.
Örneğin send(message) için varsayımlar:
messagestring olmalı,- metot gönderim sonucunu belirli biçimde döndürmeli veya açık istisna üretmeli,
- nesne gönderim için gerekli başlangıç durumuna sahip olmalı.
Bu kurallar resmî bir arayüz sınıfıyla zorunlu kılınmasa bile bir davranış sözleşmesi oluşturur.
Fonksiyonlar da polimorfik davranabilir
Python’ın yerleşik len() fonksiyonunu düşünün:
String, liste ve sözlük farklı türlerdir; ama len() hepsiyle çalışır. 12. haftada kendi sınıfımıza özel metotlar ekleyerek Python’ın bu veri modeline nasıl katılabileceğimizi göreceğiz.
Polimorfizm ve kalıtım ayrımı
Kalıtım, sınıflar arasındaki yapısal ilişkidir. Polimorfizm ise farklı nesnelerin ortak davranış üzerinden kullanılabilmesidir.
Kalıtım polimorfizmi kolaylaştırabilir:
class Payment:
def pay(self, amount):
...
class CardPayment(Payment):
...Ama Python’da ortak üst sınıf zorunlu değildir:
class CardPayment:
def pay(self, amount): ...
class CashPayment:
def pay(self, amount): ...Her ikisi de aynı pay sözleşmesini sunuyorsa ortak kod onları kullanabilir.
Soyut temel sınıf (ABC) kavram radarı
Python’da abc modülüyle soyut temel sınıf (abstract base class, ABC) tanımlanabilir. Bu, alt sınıfların belirli metotları uygulamasını daha açık bir yapıyla ifade etmeye yarar.
from abc import ABC, abstractmethod
class Payment(ABC):
@abstractmethod
def pay(self, amount):
passBu geçiş sürümünde ABC yazmak ölçme beklentisi değildir. Kavramı tanımanız yeterlidir. Önceliğimiz davranış sözleşmesini anlamaktır.
Alıştırma — Koşul zincirini kaldır
Aşağıdaki kodu inceleyin:
Tür kontrollerini kaldırıp aynı çıktıyı daha kısa ve genişletilebilir biçimde üretin.
Alıştırma — Yeni tür ekle
Yukarıdaki örneğe Bird sınıfı ekleyin. speak() metodu "Cik" döndürsün.
Tür kontrolü olmayan polimorfik sürümde ana döngüde değişiklik yapmak gerekiyor mu? Bu soruya cevap verin.
Alıştırma — Ortak davranışı tamamla
return payment.pay(amount)Küçük üretim görevi — Rapor çıktıları
Üç bağımsız sınıf yazın:
ConsoleReportTextReportCompactReport
Her biri render(data) metodu sunsun; aynı veriyi farklı string biçiminde döndürsün. Ardından:
def show_report(report, data):
return report.render(data)benzeri tek bir fonksiyonla hepsini kullanın.
Sonunda şu soruyu cevaplayın:
Bu sınıfları ortak üst sınıfa bağlamadan kullanabilmemiz hangi Python özelliğine dayanıyor?
Kendinizi kontrol edin
- Polimorfizm nedir?
- Duck typing hangi varsayıma dayanır?
- Polimorfizm için kalıtım zorunlu mudur?
- Büyüyen tür kontrolü zincirleri neden genişletilebilirliği azaltabilir?
isinstance()kullanımının her durumda yanlış olmamasının nedeni nedir?- Ortak üst sınıf olmaması davranış sözleşmesine ihtiyaç olmadığı anlamına gelir mi?
Bu haftadan akılda kalması gerekenler
- Polimorfizm, ortak çağrının farklı nesnelerde farklı davranış üretmesidir.
- Python’da duck typing sayesinde ortak üst sınıf her zaman zorunlu değildir.
- Nesnenin türünü sorgulamak yerine davranışı çağırmak çoğu zaman daha esnek tasarımdır.
- Tür kontrolü gerektiğinde kullanılabilir; asıl tasarım kokusu davranış seçiminin sürekli büyüyen tür zincirlerine bağlanmasıdır.
- Kalıtım bir ilişki; polimorfizm bir kullanım biçimidir.
- Ortak davranışın anlamı ve hata koşulları yine açık olmalıdır.