PythonProgramlamaKıdemli Python Geliştirici

Bir **Python** metaclass'ında tanımlanan hangi spesifik dunder metodu, örneklerini hedef alan `isinstance()` çağrılarını kesiyor ve bu metodun aday nesne üzerindeki rastgele niteliklere erişmesi durumunda hangi sonsuz özyineleme tehlikesi ortaya çıkıyor?

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

Sorunun cevabı.

isinstance(obj, cls) çağrıldığında, Python type(cls)'nin __instancecheck__(self, instance) tanımlayıp tanımlamadığını kontrol eder. Eğer varsa, bu metaclass metodu üyeliği belirler ve miras olmadan "sanal alt sınıflama" yapmaya olanak tanır. Tehlike, uygulamanın obj üzerinde hasattr() veya noktalı niteliğe erişim kullanması durumunda ortaya çıkar; eğer obj __getattr__ veya isinstance() kontrolünü tetikleyen tanımlayıcılar içeriyorsa, metaclass metodu tekrar girer ve sınırsız bir özyineleme meydana gelir.

Hayattan bir durum

Eklentilerin açıkça miras almadan arayüzleri karşılaması gerektiği bir doğrulama çerçevesi tasarladık. Bir Processor ABC tanımladık ve isinstance(plugin, Processor)'in plugin bir process metodu sunuyorsa başarılı olmasını istedik, bu da dış kütüphaneler için ördek tipi desteği sağladı.

İlk yaklaşım, tüm eklentilerin ya Processor'dan miras almasını ya da Processor.register()'ı çağırmasını gerektiriyordu. Bu tür güvenliydi ama kullanım durumumuz için pratik değildi; çalışma zamanında oluşturulan sınıfları engelliyordu ve değiştirilemeyecek üçüncü taraf kodunu değiştirmeyi gerektiriyordu. Sonuçta, güvensiz kaynaklardan dinamik eklenti keşfini desteklemedi.

İkinci yaklaşım, process niteliğine sahip olup olmadığını kontrol ederek Processor metaclass'ında __instancecheck__'i uyguladı. Esnek olmasına rağmen, bir eklenti tür dönüşümünü doğrulamak için isinstance kullanan bir özellik dekoratörü kullandığında RecursionError ile çöktü, metaclass metodunu yeniden çağırmadan önce ilk çağrı geri dönmeden.

Üçüncü bir çözüm benimsedik: __instancecheck__'i tanımlayıcı mantığını atlamak için object.__getattribute__ kullanarak uygulamak. type(candidate).__dict__.get('process') kontrol ederek çağrılabilir olduğuna baktık ve kullanıcı tanımlı __getattr__ veya özellik yan etkilerini tetiklemenin önüne geçtik. Bu, dinamik ördek tipini korurken, anahtar kaynak kodunun değiştirilmesine gerek kalmadan binlerce farklı eklentiyi güvenle entegre etmemizi sağladı.

class MetaProcessor(type): def __instancecheck__(cls, candidate): # Güvenli: __getattr__ ve tanımlayıcıları atlar try: attr = type(candidate).__dict__.get('process') return callable(attr) except Exception: return False class Processor(metaclass=MetaProcessor): pass

Adayların sıkça unuttuğu noktalar


Neden isinstance() ikinci argümanın metaclass'ını inceliyor, ilk argümanın metaclass'ını değil?

Protokol kontrol edilen türün (cls) otoritesini belirler, aday nesne (obj) değil. __instancecheck__'i type(cls) üzerinde koyarak, Python sadece sınıf tanımının üyelik semantiğini kontrol etmesini sağlar. Bu, nesnelerin örnek kontrollerini sahte bir şekilde geçmesini engeller; tür tek taraflı olarak neyin örneği oluşturduğunu tanımlar, güvenlik hassasiyetine sahip tür denetimlerinde bütünlüğü korur.


__instancecheck__ ve __subclasscheck__ arasındaki ilişki nedir ve neden tutarlı kalmalılar?

__instancecheck__ nesneleri isinstance() için doğrularken, __subclasscheck__ türleri issubclass() için doğrular. Eğer __instancecheck__ sanal örnekleri (sınıftan miras almayan nesneler) kabul ederse, __subclasscheck__ genellikle karşılık gelen sanal alt sınıfları kabul etmelidir; bu, isinstance(obj, cls) ifadesinin issubclass(type(obj), cls) anlamına geldiği varsayımını korumak içindir. Bunu ihlal etmek, genel konteyner kodunun örnek kontrolleri geçtikten sonra alt sınıf ilişkilerini kontrol ettiğinde başarısız olmasına neden olur.


getattr()'ın __instancecheck__ içinde kullanılması, neden object.__getattribute__'e göre sonsuz özyinelemeyi tetikler?

getattr(obj, 'name') tam tanımlayıcı protokolünü çağırır ve nitelik eksikse obj.__getattr__('name')'i çağırabilir. Eğer obj.__getattr__ tembel yükleme, günlüğe kaydetme veya içsel olarak isinstance(obj, OurClass) çağrısı yapan tür dönüşümü uygularsa, metaclass __instancecheck__ geri dönmeden önce tekrar girer. Buna karşılık, object.__getattribute__(obj, 'name') (veya type(obj).__dict__'i incelemek) __getattr__ ve özel tanımlayıcıları tamamen atlar ve kullanıcı kodunu tetiklemeden cihaza ait ayrıntılara erişir.