PythonProgramlamaKıdemli Python Geliştirici

**Python**'ın `abc` modülü, dış sınıfların `issubclass()` kontrollerini açık bir kalıtım olmadan sağlamasına hangi protokol ile izin verir ve uygulayan metodun neden kendi kendine referans yapan döngüsel kontrollerden korunması gerekir?

Hintsage yapay zeka asistanı ile mülakatları geçin

Sorunun cevabı

Python 2.6 sürümünde abc modülünü tanıtarak Soyut Temel Sınıfları (Abstract Base Classes) resmileştirdi ve geleneksel ördek tipi dışında yapısal alt türleşmeyi sağladı. Temel mekanizma, issubclass()'ın ABC'nin MRO'sunda aday bulamadığında çağırdığı __subclasshook__ sınıf yöntemidir. Bu yöntem, aday sınıfı alır ve True, False veya NotImplemented döner, böylece kalıtım olmadan sanal kayıt imkanı sağlar.

Sorun, __subclasshook__'un genellikle adayın belirli yöntemleri veya nitelikleri uygulayıp uygulamadığını doğrulaması gerektiğidir. Bir koruma koşulu olmadan, eğer kanca içsel olarak issubclass() veya benzeri kontrol çağrıları yapıp aynı ABC'ye geri dönerse, bu sonsuz bir döngüye yol açar. Zorunlu koruma, yöntemin başında if cls is MyABC kontrolü yapmayı gerektirir; bu, kökün yalnızca onu tanımlayan belirli ABC’yi doğrulamasını, o ABC’nin alt sınıflarını değil, sağlar.

from abc import ABC, abstractmethod class Drawable(ABC): @abstractmethod def draw(self): pass @classmethod def __subclasshook__(cls, C): # Döngüyü önlemek için: yalnızca Drawable’ı doğrudan ele al if cls is not Drawable: return NotImplemented # Yapısal kontrol: Drawable gibi yürüyor ve konuşuyor mu? if hasattr(C, "draw") and callable(getattr(C, "draw")): return True return NotImplemented class Circle: def draw(self): print("Daire çizme") # Kalıtım olmaksızın sanal alt sınıf doğrulaması assert issubclass(Circle, Drawable)

Hayattan bir durum

Ekibimiz, birden fazla veritabanı arka ucunu desteklemeye ihtiyaç duyan birleşik bir analiz platformu inşa ediyordu. connect(), execute() ve close() gibi yöntemleri olan bir DatabaseDriver ABC'si tanımladık. Ancak, mevcut üçüncü taraf veritabanı kütüphanelerini (örneğin psycopg2 veya pymongo) onları bölmeden veya onları işlevselliğin boğucu paketleriyle sarmadan desteklemek istedik.

İlk düşündüğümüz çözüm sıkı adaptör tasarımı kalıtımıydı. Üçüncü parti bağlantıları kapsülleyen Psycopg2Adapter(DatabaseDriver) gibi sarmalayıcı sınıflar oluşturmayı düşünüyorduk. Bu, mükemmel tür güvenliği ve statik analiz desteği sağlıyordu. Ancak, her yöntem delegasyonu için önemli bir bakım yükü oluşturdu ve çalışma zamanında çift dolaylılık yükünü ekledi.

İkinci yaklaşım, saf ördek tipi ile çalışma zamanı öznitelik incelemesi yapmaktı. connect ve execute yöntemlerine sahip herhangi bir nesnenin geçerli bir sürücü olduğunu varsayacaktık. Bu, maksimum esneklik ve hiçbir paket yükü sunarken, yöntem imzaları uyumsuz olduğunda sessizce hatalar meydana getiriyordu. Dahası, mypy gibi statik tür kontrol araçları bu sözleşmeleri doğrulayamadığı için üretim ortamlarında hata tespitinin gecikmesine yol açıyordu.

Üçüncü çözümü seçtik: DatabaseDriver ABC'mizde sanal alt sınıfları kaydetmek için __subclasshook__'u uyguladık. Bu, sarmalayıcı sınıflar ihtiyacını ortadan kaldırırken, sıkı isinstance doğrulamasını korunmasını ve üçüncü parti sınıfların değişiklik yapmadan tür kontrollerini geçmesini sağladı. Koruma koşulu, DatabaseDriver'ın kendisine karşı bir alt sınıf kontrolünün sonsuz döngüleri tetiklemeyeceğini garanti etti.

Sonuç olarak, adaptör paket kodunda %40 azalma ve sorunsuz IDE tamamlayıcı desteği elde ettik. Sistem, artık ABC'miz hakkında hiçbir şey bilmeyen kütüphanelerden ham veritabanı bağlantılarını kabul edebilirken, aynı zamanda sıkı çalışma zamanı doğrulaması ve yapısal tür garanti edebiliyordu.

Adayların sıklıkla gözden kaçırdığı noktalar

__subclasshook__ neden önce yapısal kontroller yapmadan if cls is MyABC kontrolü yapmalıdır ve bu koruma atlandığında ne olur?

Bu koruma olmadan, issubclass(SubClass, MyABC) çağrısı MyABC.__subclasshook__(SubClass)'ı tetikler. Eğer kanca içsel olarak kalıtımı doğrulamak için issubclass(SubClass, MyABC) kontrolü yapıyorsa, bu hemen sonsuz bir döngü yaratır. Python'ın abc mekanizması, yalnızca onun tanımladığı tam sınıf için kancayı çağırır, ancak yapısal kontroller genellikle aynı sorguya geri dönüş yapar. Koruma olmadan, kancanın yalnızca tanımladığı belirli ABC'yi doğrulamasını sağlamak, stack'in hızla taşmasını engellenmez.

register() ile __subclasshook__ arasında performans ve değişkenlik açısından nasıl bir etkileşim vardır?

register(), sınıfı hemen dahili önbelleğe (_abc_cache) ekler, böylece sonraki kontroller set araması ile O(1) hale gelir. Buna karşın, __subclasshook__, her issubclass çağrısında önbelleğe alınmadığı sürece rastgele Python kodunu çalıştırır, bu da hesaplama yükü yaratır. Ayrıca, register() işlem ömrü için kalıcıdır ve list gibi yerleşik türler üzerinde çalışır. Öte yandan, __subclasshook__ çalışma zamanı yeteneklerine dayanan dinamik, koşullu mantığı sağlar, ancak yalnızca kullanıcı tanımlı ABC'ler için işlev görür.

__subclasshook__ ve özel metaklaslarda __instancecheck__ metodunun etkileşimi nasıldır?

isinstance(obj, MyABC) çağrıldığında, Python önce örneğin metaklasına __instancecheck__ danışır. Eğer mevcut değilse veya sonuca ulaşamazsa, issubclass(type(obj), MyABC)'ye geri döner, bu da __subclasshook__'u tetikler. Adayların sıklıkla gözden kaçırdığı, __subclasshook__'un yalnızca sınıf kontrollerine katıldığı, doğrudan örnek kontrollerine katılmadığıdır. Ayrıca, NotImplemented döndürmenin, kontrolün MRO'da devam etmesine izin verdiğini atlamaktadır; bu, karmaşık hiyerarşilerde işbirlikçi çoklu dağıtımın önünü açar.