PythonProgramlamaKıdemli Python Geliştiricisi

Bir **Python** sınıfı miras listesinde yer aldığında, hangi dinamik yer değiştirme mekanizması, katılımcı sınıflarını belirler ve böylece Metot Çözümleme Sırası hesaplamasını etkiler?

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

Soruya cevap

Sorunun geçmişi

Python 3.7'de PEP 560 kabulü ile birlikte, tür sisteminin List[int] veya Generic[T] gibi genel türleri temel sınıflar olarak kullanabilmesi için bir yola ihtiyacı vardı. Bu iyileştirmeden önce, bir parametreli generic'ten miras almaya çalışmak TypeError ile sonuçlanıyordu çünkü bu nesneler gerçek sınıflar değildi ve geliştiricilerin kütüphane tasarımını karmaşıklaştıran komplike metaclass çözümlerine başvurmasına neden oluyordu.

Sorun

Yorumlayıcı bir sınıf tanımını işlerken, Metot Çözümleme Sırası (MRO) hesaplamak için C3 doğrusal algoritmasını kullanması gerekir. Bu algoritma, tüm temel unsurların sınıflar olmasını gerektirir. Temel nesne bir sınıf değil, bir genel takma ad olduğunda problem ortaya çıkar; yorumlayıcının, miras alma semantiklerini bozmadan MRO inşası sırasında bu takma ad için hangi gerçek sınıfların yer alması gerektiğini belirlemek için bir protokole ihtiyacı vardır.

Çözüm

Python, __mro_entries__ protokolünü tanıttı. Sınıf oluşturma, bu methoda sahip bir temelle karşılaştığında, base.__mro_entries__(original_bases) çağrılır ve bir sınıflar demeti döndürülmesi beklenir. Bu sınıflar, MRO hesaplamasında orijinal temelin yerini alır. Örneğin, typing.Generic, (Generic,) döndürmek üzere bunu uygular, böylece temel olarak işlev görürken parametreli mantık ayrı kalır.

from typing import Generic, TypeVar T = TypeVar('T') # Generic[T] bir sınıf değildir, ancak __mro_entries__ onun bir sınıf gibi davranmasına izin verir class Container(Generic[T]): pass # Container.__mro__ Generic'i içerir, Generic[T] içermemektedir print(Container.__mro__) # (<class 'Container'>, <class 'typing.Generic'>, <class 'object'>)

Gerçek hayattan bir durum

Bir çerçeve ekibi, kullanıcıların Model[UserType] gibi parametreli genel temeller kullanarak veri modelleri tanımlamalarına olanak tanımak zorundaydı. İlk yaklaşımları, sınıf oluşturma sürecini kesmek ve tür parametrelerini çıkarmak için özel bir metaclass kullanmak oldu, ancak bu, kullanıcıların çerçeveyi Django veya SQLAlchemy modelleriyle birleştirirken metaclass çatışmalarını manuel olarak çözmesini zorunlu kıldı.

Sınıf tanımından sonra sınıfı yeniden yazmak için bir sınıf dekoratörü kullanmayı düşündüler, ancak bu yaklaşım, dönüşümün tür kontrolörü kaynak kodunu analiz ettikten sonra gerçekleşmesi nedeniyle statik tür kontrolünü ve IDE otomatik tamamlama işlevselliğini bozdu. Başka bir alternatif olan __init_subclass__, temelin kendisi bir sınıf olmadığında durumu ele alamadı.

Ekip, genel fabrika nesnelerinde __mro_entries__'i uyguladı. Kullanıcılar class UserModel(Model[UserType]) yazdığında, Model[UserType] örneği __mro_entries__ methodundan (Model,) döndürdü. Bu, sınıfın Model'den doğru bir şekilde miras almasını sağlarken, fabrika tür parametresini çalışma zamanı doğrulaması için sakladı. Çözüm metaclass çatışmalarını ortadan kaldırdı, tam IDE desteğini korudu ve C3 doğrusal algoritmasının gereksinimlerini karşılayan temiz bir miras hiyerarşisini sürdürdü.

Adayların sıklıkla atlattığı noktalar

__mro_entries__, çalışma zamanı tür kontrolünü veya isinstance davranışını etkiler mi?

Adaylar genellikle MRO inşasını örnek kontrolü ile karıştırırlar. __mro_entries__, __mro__ demetini oluşturmak için yalnızca sınıf oluşturma sırasında çalışır. Çalışma zamanındaki isinstance() veya issubclass() kontrolleri üzerinde hiçbir etkisi yoktur. Bu işlemler, mevcut sınıfların __class__ ve __bases__ niteliklerine dayanır, sınıf tanım aşamasında meydana gelen dinamik yer değiştirme ile alakalı değildir.

Neden __mro_entries__ tek bir sınıf yerine bir demet döndürür?

Demet dönüş tipi, karmaşık çoklu miras senaryolarını karşılamak için tasarlanmıştır. Genellikle (Generic,) gibi tek elemanlı bir demet döndürebilmesine rağmen, protokol, bir genel parametrenin aynı anda birden fazla mixin'den miras almayı ima etmesine olanak tanır. Python, bu demeti doğrudan temel listesinin içerisine yerleştirir, bu nedenle (A, B) döndürmek, sınıfın hem A hem de B'den miras almasını sağlar, orijinal sınıf olmayan temel yerine.

__mro_entries__ tarafından döndürülen sınıflar üzerinde Python hangi doğrulamayı yapar?

Yorumlayıcı, döndürülen sınıfların geçerli bir miras grafiği oluşturduğunu titizlikle doğrular. Eğer demet, MRO'da tutarsızlık yaratacak sınıflar içeriyorsa—örneğin, C3 doğrusal kısıtlarını ihlal eden bir elmas miras çatışması getiriyorsa—Python, sınıf oluşturma sırasında TypeError yükseltir. Bu doğrulama, dinamik yer değiştirmenin dilin temel miras tutarlılığı kurallarını aşamamış olduğunu garanti eder.